Quarto Integration Guide¶
Embed interactive Panel and Python examples in your Quarto documents using panel-live.
Prerequisites¶
- Quarto >= 1.3.0
- A web browser with WebAssembly support
Installation¶
Install the extension from the panel-live repository:
This copies the Lua filter, JS/CSS assets, and extension metadata into your project's _extensions/ directory.
Manual Installation¶
Alternatively, copy the extension files manually:
your-project/
_extensions/
panel-live/
_extension.yml
panel-live.lua
panel-live.js
panel-live-worker.js
panel-live.css
mini-coi.js
Configuration¶
Project-Level (_quarto.yml)¶
filters:
- panel-live
panel-live:
pyodide-version: "v0.28.2"
panel-version: "1.8.7"
bokeh-version: "3.8.2"
JS and CSS assets are bundled with the extension — no CDN configuration is needed.
Per-Document (YAML Front Matter)¶
Override versions for a specific document:
For a working reference example, see docs-quarto/_quarto.yml.
Code Block Syntax¶
Basic¶
Use a fenced code block with the .panel-live class (the dot prefix is required):
The .panel class also works as an alias:
With #| Directives¶
Configure attributes using Quarto's #| directive syntax:
```{.panel-live}
#| 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 |
requirements |
package names | — | Python packages to install |
Expression Mode (No .servable())¶
Code without .servable() uses the expression branch — the last expression is rendered as output:
```{.panel-live}
#| mode: editor
#| label: Python
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
fig
```
Version Configuration¶
Versions can be configured at the project level in _quarto.yml or per-document in YAML front matter:
panel-live:
pyodide-version: "v0.28.2"
panel-version: "1.8.7"
bokeh-version: "3.8.2"
panel-cdn: "https://cdn.holoviz.org/panel/"
bokeh-cdn: "https://cdn.bokeh.org/bokeh/release/"
Cross-Origin Headers (COOP/COEP)¶
Pyodide requires SharedArrayBuffer, which needs these HTTP headers:
See the MkDocs Integration Guide for details on configuring headers for various hosting providers, including the mini-coi service worker approach.
Migration from holoviz-quarto¶
If you're migrating from holoviz-quarto:
Step 1: Install panel-live Extension¶
Step 2: Update _quarto.yml¶
# Before (holoviz-quarto)
filters:
- holoviz-quarto
# After (panel-live)
filters:
- panel-live
panel-live:
pyodide-version: "v0.28.2"
panel-version: "1.8.7"
bokeh-version: "3.8.2"
Step 3: Update Code Blocks¶
```Non-HTML Output¶
For non-HTML output formats (PDF, DOCX, etc.), the filter passes through the original code block without transformation.
Troubleshooting¶
"SharedArrayBuffer is not defined"¶
Missing COOP/COEP headers. See the Cross-Origin Headers section.
Code blocks render as plain code¶
Ensure the filter is listed in _quarto.yml and the extension is properly installed in _extensions/panel-live/. Use the dot-prefix syntax {.panel-live} — without the dot, Quarto treats the code block as a computational cell for an unknown engine instead of passing it to the Lua filter.
Extension not found¶
Check that _extensions/panel-live/panel-live.lua and _extensions/panel-live/_extension.yml exist in your project directory.