Converting from AnyWidget to Panel#
This guide addresses how to convert AnyWidget widgets to custom JSComponent
or ReactComponent
widgets.
Please note that AnyWidget widgets are ipywidgets
and can be used directly in Panel via the IpyWidgets
pane and the Traitlets
@observe
API without a conversion. We recommend trying this option first. If it does not work for your use case, please consider contributing to the existing AnyWidget
before converting it to a Panel widget.
Some reasons you might still want to convert an AnyWidget
to a custom Panel widget are:
Familiar and Optimized API: This enables you and your users to use the familiar
Param
parameter API for which Panel is optimized.Customization: You might want to use the
AnyWidget
as a starting point and customize it to your exact needs.Efficiency: You users avoid loading
AnyWidget
/ipywidgets
JavaScript libraries which ANECDOTALLY is not insignificant. Your users also avoid the overhead of converting betweenParam/Panel/Bokeh
andTraitlets
/AnyWidget
/ipywidgets
objects: ANECDOTALLY, Panel (i.e., Bokeh) utilizes faster serialization and deserialization methods and formats. IS THIS TRUE, PHILIPP? ALSO, WHEN USING THE ANYWIDGET ON THE BOKEH SERVER? DO WE HAVE NUMBERS FOR THIS?
Conversion Steps#
The high-level steps needed for converting AnyWidgets
components to Panel components are described in the section below.
Convert Python Code#
Step 1: Base Class Conversion#
Convert from the AnyWidget
base class to the Panel JSComponent
base class. If the _esm
script is based on React, use the ReactComponent
.
Step 2: Attribute Conversion#
Convert from AnyWidget
’s Traitlets based attributes to Panel’s Param based parameters.
Convert the attributes below:
|
Comment |
|
Comment |
---|---|---|---|
|
JavaScript string or Path |
|
JavaScript string or Path |
|
CSS string or Path |
|
List of CSS strings or Paths |
NA |
Not Available |
|
Convert JavaScript Code#
Step 3: Export Conversion#
Convert the functions below:
|
Comment |
|
Comment |
---|---|---|---|
|
Has access to |
NA |
Not Available |
|
Has access to |
|
Has access to |
In the _esm
script, either use the default
export or named exports.
Their methods are also different reflecting differences between Traitlets and Panel/ Bokeh JavaScript models:
AnyWidget |
Panel/ Bokeh |
---|---|
|
|
|
|
|
|
Convert React Code#
Step 4: Drop JavaScript Tooling#
Drop the local JavaScript tooling required by AnyWidget
. Panel replaces this with automatic transpiling in the browser by Sucrase.
Step 5: Convert useModelState
to state
#
The ReactComponent
_esm
script works similarly to the JSComponent
_esm
script with the following differences:
|
Comment |
|
Comment |
---|---|---|---|
|
Can use imports from |
|
Can use hooks like |
Examples#
Basic CounterWidget
#
AnyWidget CounterWidget
#
import anywidget
import traitlets
class CounterWidget(anywidget.AnyWidget):
value = traitlets.Int(0).tag(sync=True)
_esm = """
function render({ model, el }) {
let count = () => model.get("value");
let btn = document.createElement("button");
btn.innerHTML = `count is ${count()}`;
btn.addEventListener("click", () => {
model.set("value", count() + 1);
model.save_changes();
});
model.on("change:value", () => {
btn.innerHTML = `count is ${count()}`;
});
el.appendChild(btn);
}
export default { render };
"""
Panel JS CounterWidget
#
import panel as pn
import param
from panel.custom import JSComponent
pn.extension()
class CounterButton(JSComponent):
value = param.Integer()
_esm = """
export function render({ model }) {
let count = () => model.value;
let btn = document.createElement("button");
btn.innerHTML = `count is ${count()}`;
btn.addEventListener("click", () => {
model.value += 1
});
model.on('change:value', () => {
btn.innerHTML = `count is ${model.value}`;
})
return btn
}
"""
# Display the component
CounterButton().servable()
Note
With Panel you may replace the lines export function render({ model })
and return btn
with the lines export function render({ model, el })
and el.appendChild(btn)
, if you want to minimize the number of changes.
Panel React CounterWidget
#
import panel as pn
import param
from panel.custom import ReactComponent
pn.extension()
class CounterButton(ReactComponent):
value = param.Integer()
_esm = """
export function render({ model }) {
const [value, setValue] = model.useState("value");
return (
<button onClick={e => setValue(value+1)}>
count is {value}
</button>
);
}
"""
# Display the component
CounterButton().servable()
References#
Tutorials#
How-To Guides#
Reference Guides#
With these skills, you are now equipped to pioneer and push the boundaries of what can be achieved with Panel. Happy coding!