Using Panel in Pyodide & PyScript#

Pyodide#

Creating a Basic Panel Pyodide Application#

Create a file called script.html with the following content:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.6.1.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.6.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.6.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@1.5.4a2/dist/panel.min.js"></script>
  </head>
  <body>
    <div id="simple_app"></div>
    <script type="text/javascript">
      async function main() {
        let pyodide = await loadPyodide();
        await pyodide.loadPackage("micropip");
        const micropip = pyodide.pyimport("micropip");
        await micropip.install([
          "https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/bokeh-3.6.1-py3-none-any.whl",
          "https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/panel-1.5.4a2-py3-none-any.whl"
        ]);
        pyodide.runPython(`
          import panel as pn
          pn.extension(sizing_mode="stretch_width")
          slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')
          def callback(new):
              return f'Amplitude is: {new}'

          pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app');
      `);
      }
      main();
    </script>
  </body>
</html>

Serve the app with:

python -m http.server

Open the app in your browser at http://localhost:8000/script.html.

The app should look like this:

Panel Pyodide App

warn

The default Bokeh and Panel packages are very large. Therefore we recommend installing specialized wheels:

const bk_whl = "https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/bokeh-3.6.1-py3-none-any.whl";
const pn_whl = "https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/panel-1.5.4a2-py3-none-any.whl";
await micropip.install(bk_whl, pn_whl);

PyScript#

Creating a Basic Panel PyScript Application#

Create a file called script.html with the following content:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.6.1.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.6.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.6.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@1.5.4a2/dist/panel.min.js"></script>
    <link rel="stylesheet" href="https://pyscript.net/releases/2024.8.1/core.css">
    <script type="module" src="https://pyscript.net/releases/2024.8.1/core.js"></script>
  </head>
  <body>
    <div id="simple_app"></div>
    <script type="py" config='{"packages": ["https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/bokeh-3.6.1-py3-none-any.whl", "https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/panel-1.5.4a2-py3-none-any.whl"]}'>
      import panel as pn
      pn.extension(sizing_mode="stretch_width")
      slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')
      def callback(new):
         return f'Amplitude is: {new}'
      pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app')
    </script>
  </body>
</html>

Serve the app with:

python -m http.server

Open the app in your browser at http://localhost:8000/script.html.

The app should look like this:

Panel Pyodide App

The PyScript documentation recommends separating your configuration and Python code into different files. Examples can be found in the PyScript Examples Gallery.

Creating a Basic py-editor Example#

Create a file called script.html with the following content:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <script src="./mini-coi.js" scope="./"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.6.1.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.6.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.6.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@1.5.4a2/dist/panel.min.js"></script>

    <link rel="stylesheet" href="https://pyscript.net/releases/2024.8.1/core.css">
    <script type="module" src="https://pyscript.net/releases/2024.8.1/core.js"></script>
  </head>
  <body>
    <script type="py-editor" config='{"packages": ["https://cdn.holoviz.org/panel/wheels/bokeh-3.6.1-py3-none-any.whl", "https://cdn.holoviz.org/panel/1.5.4a2/dist/wheels/1.5.4a2-py3-none-any.whl"]}'>
      import panel as pn

      pn.extension(sizing_mode="stretch_width")

      slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')

      def callback(new):
         return f'Amplitude is: {new}'

      pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app');
    </script>
    <div id="simple_app"></div>
  </body>
</html>

Create a file called mini-coi.js with the content from mini-coi.js.

Serve the app with:

python -m http.server

Open the app in your browser at http://localhost:8000/script.html.

Click the green run button that appears when you hover over the lower-right corner of the editor to see the application.

In the example, we included mini-coi.js. This is not necessary if the appropriate HTTP headers are set on your server, such as on pyscript.com or in Github pages.

Rendering Panel Components in Pyodide or PyScript#

Rendering Panel components into the DOM is straightforward. Use the .servable() method on any component and provide a target that matches the id of a DOM node:

import panel as pn

slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')

def callback(new):
    return f'Amplitude is: {new}'

pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app')

This code will render the application into the simple_app DOM node:

<div id="simple_app"></div>

Alternatively, you can use the panel.io.pyodide.write function to write into a specific DOM node:

await pn.io.pyodide.write('simple_app', component)