MkDocs Integration Guide¶
Embed interactive Panel and Python examples in your MkDocs documentation using panel-live.
Prerequisites¶
- Python 3.10+
- MkDocs with Material for MkDocs theme (recommended)
pymdownx.superfencesextension
Installation¶
Configuration¶
For a working reference example checkout panel-extensions/panel-live/zensical.toml.
Configure a Custom Fence¶
Add the custom fence to your mkdocs.yml:
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: panel
class: panel-live
validator: !!python/name:panel_live.fences.validator
format: !!python/name:panel_live.fences.formatter
Or if using zensical (TOML-based MkDocs config):
[project.markdown_extensions.pymdownx.superfences]
custom_fences = [
{ name = "panel", class = "panel-live", validator = "panel_live.fences.validator", format = "panel_live.fences.formatter" }
]
Loading the Web Component¶
Add the panel-live JavaScript and CSS to your site. You can use the CDN:
extra_javascript:
- https://cdn.jsdelivr.net/npm/panel-live@latest/dist/panel-live.js
extra_css:
- https://cdn.jsdelivr.net/npm/panel-live@latest/dist/panel-live.css
Or copy the files locally (recommended for reproducibility):
Fence Syntax¶
Basic¶
Use a fenced code block with the panel language identifier:
With Attributes¶
Use the {.panel ...} syntax to pass attributes:
```{.panel mode="editor" theme="dark" height="500px"}
import panel as pn
pn.panel("Hello").servable()
```
Available Attributes¶
| Attribute | Values | Default | Description |
|---|---|---|---|
mode |
app, editor, playground |
app |
Display mode |
theme |
light, dark, auto |
auto |
Color theme |
height |
CSS value (e.g. 500px) |
— | Fixed height |
layout |
vertical, horizontal |
mode-dependent | Editor/preview layout |
auto-run |
true, false |
true |
Run code on page load |
label |
any string | Python |
Language pill label |
code-visibility |
visible, hidden, collapsed |
visible |
Editor visibility |
code-position |
first, last |
first |
Code panel position |
Expression Mode (No .servable())¶
Code without .servable() uses the expression branch — the last expression is rendered as output. This works with any displayable Python object:
```{.panel mode="editor" label="Python"}
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
fig
```
Cross-Origin Headers (COOP/COEP)¶
Pyodide requires SharedArrayBuffer, which needs these HTTP headers:
Local Development¶
The panel-live dev server (pixi run serve or python serve.py) adds these headers automatically.
GitHub Pages¶
GitHub Pages does not support custom headers. Workarounds:
- Service Worker approach — Use coi-serviceworker to enable COOP/COEP via a service worker
- Cloudflare Pages — Supports custom headers via
_headersfile
Netlify¶
Add a _headers file to your docs directory:
Other Hosts¶
Most hosting providers support custom headers via configuration files or server settings. Consult your provider's documentation.
mini-coi (No Server Configuration Required)¶
If you cannot configure COOP/COEP headers on your hosting provider (e.g., GitHub Pages), mini-coi provides SharedArrayBuffer support via a lightweight service worker:
- Download
mini-coi.jsto your docs directory - Add it to
mkdocs.ymlbefore panel-live.js:
mini-coi registers a service worker that adds COOP/COEP headers transparently. This avoids the need for server-side configuration and works on any static hosting provider.
When to use mini-coi:
- GitHub Pages (no custom header support)
- Static hosting without server configuration access
- Quick local testing without
serve.py
Note: If you control the server, native HTTP headers (see above) are preferred as they are more robust and don't require JavaScript.
Troubleshooting¶
"SharedArrayBuffer is not defined"¶
Missing COOP/COEP headers. See the Cross-Origin Headers section.
Code blocks render as plain text¶
Ensure pymdownx.superfences is configured with the custom fence and that panel-live.js is loaded on the page.
Pyodide fails to load¶
Check browser console for errors. Common causes:
- Ad blockers or tracking prevention blocking CDN resources
- Corporate proxy blocking WebAssembly downloads
- Outdated browser without WebAssembly support
Multiple panel-live blocks on one page¶
All blocks share a single Pyodide runtime. The first block to initialize loads Pyodide; subsequent blocks reuse it. Execution is serialized (one at a time) to avoid conflicts.