Build your own custom components with ReactiveHTML

Initially, I was hesitant to try ReactiveHTML but now that I’ve learned it I feel like it’s so powerful–I can build so many custom components easily.

As a simple example, I’ve always wanted a slimmer version of pn.Card:

TLDR Full Code:

import param
import panel as pn
pn.extension()

class Details(pn.reactive.ReactiveHTML):
    title = param.String()
    contents = param.Parameter()

    _template = """
        <details>
            <summary>${title}</summary>
            ${contents}
        </details>
    """


details = Details(
    title="Hey click me!", contents="Just wanted to show you something cool!"
)
details

Tutorial:

  1. I look up how to add collapsible sections in Markdown

  2. I paste the gist of it into a ReactiveHTML _template

class Details(pn.reactive.ReactiveHTML):

    _template = """
        <details>
            <summary>Click me</summary>
            Contents
        </details>
    """
  1. I replace whatever needs to be changeable with ${parameter_name}:

class Details(pn.reactive.ReactiveHTML):

    _template = """
        <details>
            <summary>${title}</summary>
            ${contents}
        </details>
    """
  1. I add those parameters to the class.
class Details(pn.reactive.ReactiveHTML):

    title = param.String()
    contents = param.Parameter()

    _template = """
        <details>
            <summary>${title}</summary>
            ${contents}
        </details>
    """
  1. Try it out!

  2. And you can update the title/contents dynamically!
    image

4 Likes

Wrapping a div + id around contents allows panel objects to be embedded too:

import param
import panel as pn

pn.extension()


class Details(pn.reactive.ReactiveHTML):
    title = param.String()
    contents = param.ClassSelector(class_=object)

    _template = """
        <details>
            <summary>${title}</summary>
            <div id="inner">${contents}</div>
        </details>
    """

details = Details(
    title="Hey click me!",
    contents="Just wanted to show you something cool!"
)
details

details.contents = pn.indicators.LoadingSpinner()

Unfortunately, this collapses the expanded contents.

Unfortunately can’t add open dynamically

It’s actually doable with a kwarg; thanks @Marc!

import param
import panel as pn

pn.extension()


class Details(pn.reactive.ReactiveHTML):
    title = param.String()
    contents = param.ClassSelector(class_=object)
    open = param.Boolean()

    _template = """
        <details id="details" open=${open}>
            <summary>${title}</summary>

            <div id="inner">${contents}</div>

        </details>
    """

details = Details(
    title="Hey click me!",
    contents="Just wanted to show you something cool!",
    open=False
)
pn.Column(
    details, details.param.open
).servable()
3 Likes

Tutorial here:
https://blog.holoviz.org/building_custom_panel_widgets_using_reactivehtml.html

1 Like

Nice blog post! More of these please!

One suggestion in case you are interested: Explain a bit more in detail what is we are seeing in each example gif. It may not be obvious to someone that does not already know what to expect.

1 Like