# Share an Observable Plot — Without the Notebook or the Account

Canonical: https://commareports.com/share-observable-plot
Published: 2026-09-14

> Observable Plot renders to a DOM node, not a file. Wrap it in one HTML page, publish to Comma, and the chart is a URL with tooltips and interaction intact.

# Share an Observable Plot

Observable Plot is the rare visualization library with no runtime dependency on
a platform. It is a normal npm package that returns a DOM node — which makes it
much easier to share than a notebook, and much easier to fail to share than a
chart image, because "a DOM node" is not a thing you can email.

The result is that Plot charts get screenshotted more than any other kind, and
the tooltip — the whole reason to use `tip: true` — never leaves the machine
that made it.

## Make it a page

```html
<!doctype html>
<meta charset="utf-8" />
<title>Signups by week</title>
<div id="chart"></div>
<script type="module">
  import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm";

  const data = [
    /* inlined rows */
  ];

  document.querySelector("#chart").append(
    Plot.plot({
      marks: [
        Plot.ruleY([0]),
        Plot.lineY(data, { x: "week", y: "signups", stroke: "channel", tip: true }),
      ],
      width: 900,
    }),
  );
</script>
```

That is the entire artifact: one file, one script, the data inline.

## Publish it

Drop the file into [Comma](https://commareports.com/), or from a build step:

```bash
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html chart.html \
        --arg t "Signups by channel — $(date -u +%F)" '{title: $t, html: $html}')"
```

Scripts run inside a sandboxed iframe (`allow-scripts`, no `allow-same-origin`),
so the module loads, the marks render and `tip: true` tooltips follow the
pointer. See the [API reference](/docs/api).

## Inline the data and the library

Two changes turn a chart that works today into one that works in a year:

- **Inline the data.** A `fetch("data.csv")` is a dependency on a file existing
  at a path. Under a megabyte, put the rows in the page. Larger, upload the CSV
  as a report asset — relative references are rewritten to the uploaded copy, so
  the fetch resolves over `https://` instead of failing from a `file://` origin.
- **Bundle Plot.** `@0.6` from a CDN is a moving target and a third party you
  have no agreement with. `esbuild --bundle --minify` over an entry that imports
  Plot is a one-line build step and removes both problems.

## What it is worth over a screenshot

A Plot chart with a `tip` and a colour legend is a small interface: hover for
the exact value, click the legend to isolate a series, read the axis at the
resolution you need. A PNG is one of those states, chosen by whoever took it,
usually the wrong one for the question being asked.

Published, the reader answers their own question — and the follow-up lands as
an anchored thread on the series it is about rather than as "which line is
which?" in a reply. See [commenting on HTML](/comment-on-html).

Republishing at the same report id when the data refreshes appends a revision,
so the chart has a history rather than a filename with a date in it.

## Limits

- **Entry HTML: 5 MB**, inlined data included. Pre-aggregate rather than
  shipping raw rows the mark immediately bins.
- Assets: 25 MB per file, **250 MB and 500 files total**.
- **60 requests/minute per token.**

## Try it

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

**[Publish a chart →](https://commareports.com/)**

### Related

- [Share a D3 visualization](/share-d3-visualization) · [Share a Vega-Lite chart](/share-vega-lite-chart)
- [Share an Observable notebook](/share-observable-notebook) · [Share a Plotly HTML chart](/share-plotly-html)
- [Share a Chart.js chart](/share-chartjs-chart) · [Interactive HTML reports](/interactive-html-reports)
