Skip to content

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:

quarto add panel-extensions/panel-live --subdir quarto

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:

---
title: "My Document"
panel-live:
  panel-version: "1.9.0a1"
---

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):

```{.panel-live}
import panel as pn
pn.panel("Hello").servable()
```

The .panel class also works as an alias:

```{.panel}
import panel as pn
pn.panel("Hello").servable()
```

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:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

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

quarto add panel-extensions/panel-live --subdir quarto

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

<!-- Before -->
```{.holoviz}
import panel as pn
pn.panel("Hello").servable()

import panel as pn
pn.panel("Hello").servable()
```

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.