# Share a Panel App as HTML — Interactive Dashboards Without a Server

Canonical: https://commareports.com/share-panel-app
Published: 2026-09-01

> Panel's .save() writes a self-contained HTML file with the widgets embedded. Publish it to Comma so a dashboard is a link, with anchored comments and a revision each time the data refreshes.

# Share a Panel app as HTML

Panel's appeal is that a few lines of Python become a real dashboard. Its
cost is that a real dashboard is a running server, and a running server
is a deployment, a URL, an auth story and someone's ongoing problem.

For a large share of dashboards — the weekly number, the model
evaluation, the ops summary — nobody needs live Python. They need
today's version, with the controls working, at a link.

## Publish it

```python
import panel as pn

dashboard.save("dashboard.html", embed=True, max_states=16)
```

```bash
curl -X POST https://commareports.com/api/v1/reports \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html dashboard.html \
        '{title: "Ops dashboard — week 36", html: $html}')"
```

Or drag `dashboard.html` into [the app](https://commareports.com/).

The widgets keep working at the link — report HTML runs scripts inside a
sandboxed iframe (`allow-scripts`, no `allow-same-origin`).

## What `embed=True` can and cannot do

It precomputes the output for each combination of widget states. Discrete
selectors embed well; continuous sliders get sampled; and the file grows
with the product of the options, so a dashboard with five interacting
widgets can become enormous. Narrow the widget set for the shared
version — the shared version usually wants fewer knobs anyway.

Anything requiring Python at interaction time — a live database query,
inference on new input — is not in a static export. Put the Python on a
schedule instead.

## Keep it current on a routine

A [routine](/docs/routines) re-runs the script and PATCHes the same
report, so the URL holds current numbers rather than the state of the
world when someone last remembered to re-export. See
[scheduled HTML reports](/features/routines/scheduled-html-reports).

## Limits

- **HTML body: 5 MB.** `embed=True` with a wide state space passes it
  quickly — lower `max_states`, or embed fewer widgets.
- **Scripts run, sandboxed**: `allow-scripts`, no `allow-same-origin`.
- **60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history.

**[Create your first report →](https://commareports.com/)**

### Related

- [HoloViews plots](/share-holoviews-plot) — the charts underneath
- [Bokeh plots](/share-bokeh-plot) · [Plotly HTML](/share-plotly-html)
- [Weekly digests](/features/routines/weekly-analytics-digest) · [Jupyter](/with/jupyter)
