Create Layouts With ReactiveHTML#
In this guide we will show you how to build custom layouts using HTML and ReactiveHTML.
Layout a single parameter#
You can layout a single object as follows.
import panel as pn
import param
from panel.custom import Child, ReactiveHTML
pn.extension()
class LayoutSingleObject(ReactiveHTML):
object = Child(allow_refs=False)
_template = """
<div>
<h1>Temperature</h1>
<h2>A measurement from the sensor</h2>
<div id="object">${object}</div>
</div>
"""
dial = pn.widgets.Dial(
name="°C",
value=37,
format="{value}",
colors=[(0.40, "green"), (1, "red")],
bounds=(0, 100),
)
LayoutSingleObject(
object=dial,
name="Temperature",
styles={"border": "2px solid lightgray"},
sizing_mode="stretch_width",
).servable()
Note
We define the HTML layout in the
_templateattribute.We can refer to the parameter
objectin the_templatevia the template parameter${object}.We must give the
divelement holding the${object}anid. If we do not, then an exception will be raised. Theidcan be any value, for exampleid="my-object".
We call our object parameter
objectto be consistent with our built in layouts. But the parameter can be called anything. For examplevalue,dialortemperature.We add the
borderin thestylesparameter so that we can better see how the_templatelayes out inside theReactiveHTMLcomponent. This can be very useful for development.
Layout multiple parameters#
import panel as pn
import param
from panel.custom import Child, ReactiveHTML
pn.extension()
class LayoutMultipleValues(ReactiveHTML):
object1 = Child()
object2 = Child()
_template = """
<div>
<h1>Object 1</h1>
<div id="object1">${object1}</div>
<h1>Object 2</h1>
<div id="object2">${object2}</div>
</div>
"""
layout = LayoutMultipleValues(
object1="This is the **value** of `object1`", object2="This is the **value** of `object2`",
styles={"border": "2px solid lightgray"},
)
layout.servable()
You might notice that the values of object1 and object2 looks like they have been
rendered as markdown! That is correct.
Before inserting the value of a parameter in the _template, Panel transforms the value using pn.panel. And for a string value pn.panel returns a Markdown pane.
Let’s verify this.
print(type(layout.object1), type(layout.object2))
<class 'panel.pane.markup.Markdown'> <class 'panel.pane.markup.Markdown'>
Lets for fun try another example
LayoutMultipleValues(
object1="Do you like **beat boxing**?",
object2="https://upload.wikimedia.org/wikipedia/commons/d/d3/Beatboxset1_pepouni.ogg",
styles={"border": "2px solid lightgray"},
)
Layout as literal str values#
If you want to show the literal str value of your parameter instead of the pn.panel return value you can configure that via the _child_config attribute.
import panel as pn
import param
from panel.custom import ReactiveHTML
pn.extension()
class LayoutLiteralValues(ReactiveHTML):
object1 = param.String()
object2 = param.String()
_child_config = {"object1": "literal", "object2": "literal"}
_template = """
<style>
.pn-container {height: 100%;width: 100%;}
</style>
<div class="pn-container">
<h1>Object 1</h1>
<div id="object1">${object1}</div>
<h1>Object 2</h1>
<div id="object2">${object2}</div>
</div>
"""
layout = LayoutLiteralValues(
object1="This is the **value** of `object1`", object2="This is the **value** of `object2`",
styles={"border": "2px solid lightgray"},
)
layout.servable()
Lets check the types
print(type(layout.object1), type(layout.object2))
<class 'panel.pane.markup.Markdown'> <class 'panel.pane.markup.Markdown'>
Layout a list of objects#
If you want to want to layout a dynamic List of objects you can use a for loop.
import panel as pn
import param
from panel.custom import Children, ReactiveHTML
pn.extension()
class LayoutOfList(ReactiveHTML):
objects = Children()
_template = """
<div id="container" class="pn-container">
{% for object in objects %}
<h1>Object {{ loop.index0 }}</h1>
<div id="object">${object}</div>
<hr/>
{% endfor %}
</div>
"""
LayoutOfList(objects=[
"I **love** beat boxing",
"https://upload.wikimedia.org/wikipedia/commons/d/d3/Beatboxset1_pepouni.ogg",
"Yes I do!"
], styles={"border": "2px solid lightgray"}).servable()
The component will trigger a rerendering if you update the List value.
Note
You must
wrap the
{% for object in objects %}loop in an HTML element with anid. Here it is wrapped with<div id="container">...</div>.close all HTML tags!
<hr>is valid HTML, but not valid withReactiveHTML. You must close it as<hr/>.
You can optionally
get the index of the
{% for object in objects %}loop via{{ loop.index0 }}.
Create a list like layout#
If you want to create a list like layout similar to Column and Row, you can combine ListLike and ReactiveHTML.
import panel as pn
import param
from panel.custom import ReactiveHTML
from panel.layout.base import ListLike
pn.extension()
class ListLikeLayout(ListLike, ReactiveHTML):
objects = param.List()
_template = """
<div id="container" class="pn-container">
{% for object in objects %}
<h1>Object {{ loop.index0 }}</h1>
<div id="object">${object}</div>
<hr/>
{% endfor %}
</div>
"""
layout = ListLikeLayout(
"I love beat boxing",
"https://upload.wikimedia.org/wikipedia/commons/d/d3/Beatboxset1_pepouni.ogg",
"Yes I do!",
styles={"border": "2px solid lightgray"},
)
layout.servable()
You can now use [...] indexing and the .append, .insert, pop, … methods that you would
expect.
Note
You must list ListLike, ReactiveHTML in exactly that order when you define the class! The other
way around ReactiveHTML, NamedListLike will not work.
Layout a dictionary#
If you want to layout a dictionary, you can use a for loop on the .items().
import panel as pn
import param
from panel.custom import ReactiveHTML
pn.extension()
class LayoutOfDict(ReactiveHTML):
object = param.Dict()
_template = """
<div id="container" class="pn-container">
{% for key, value in object.items() %}
<h1>{{ loop.index0 }}. {{ key }}</h1>
<div id="value">${value}</div>
<hr/>
{% endfor %}
</div>
"""
LayoutOfDict(object={
"Intro": "I **love** beat boxing",
"Example": "https://upload.wikimedia.org/wikipedia/commons/d/d3/Beatboxset1_pepouni.ogg",
"*Outro*": "Yes I do!"
}, styles={"border": "2px solid lightgray"}).servable()
Note
We can insert the
keyas a literal value only using{{ key }}. Inserting it as a template variable${key}will not work.We must not give the HTML element containing
{{ key }}anid. If we do, an exception will be raised.