Blog
Observable Plot: A Quick Guide
Observable Plot doesn't get talked about as much as D3 or the big Python libraries, which is a shame — it occupies a genuinely useful middle ground. It's built by the same team behind D3, but designed for the much more common case: you have a dataframe-shaped dataset and you want a good chart in a few lines, not a bespoke visualization system.
What makes it different
Where D3 asks you to build a chart from primitives, Plot asks you to describe one declaratively — closer to how you'd think about a chart in ggplot2 or Vega-Lite, but native to JavaScript.
import * as Plot from "@observablehq/plot";
const chart = Plot.plot({
marks: [Plot.lineY(data, { x: "quarter", y: "revenue", stroke: "region" })],
y: { grid: true },
color: { legend: true },
});
Compare that to the D3 equivalent — scales, axes, and a .join() call, all written by hand — and it's obvious what Plot is buying you: the same rendering foundation as D3, without redoing the boilerplate every time.
Where it actually fits
Plot isn't trying to replace D3 for genuinely custom visualizations, and it isn't trying to replace matplotlib for static, publication-grade output. It's aimed squarely at the case that comes up constantly and gets underserved by both: a standard chart type, real interactivity (tooltips, faceting), and JavaScript-native output that can live in a browser without an export step.
A few things it handles well out of the box that are usually painful elsewhere:
- Faceting — splitting one chart into a grid by category, without manually managing subplots
- Automatic legends — color and size scales get legends without extra code
- Tooltips via
Plot.tip— hover detail without wiring up your own event handlers
Plot.plot({
marks: [
Plot.barY(data, { x: "region", y: "revenue", fill: "region" }),
Plot.tip(data, Plot.pointer({ x: "region", y: "revenue" })),
],
facet: { data, x: "quarter" },
});
The gap that's still there
Like D3, Plot solves the chart problem, not the delivery problem. It renders to SVG or HTML in a browser context — which means it needs a browser context to actually be seen. A chart built with Plot in a notebook or a local script still needs somewhere to live that isn't "a screenshot" or "a file someone has to open correctly."
Because Plot's output is standard SVG under the hood, it slots into the same flat scene graph as any other visual node — no wrapper, no special case. A Plot chart, a D3 chart, and a markdown block can all sit on the same canvas, get published together, and stay live against the same underlying query.
When to reach for it
If you're working in JavaScript, want more polish than raw D3 without hand-rolling scales and axes, and don't need something D3-custom — Plot is very likely the right default. Just plan for where the chart goes once it's built, the same way you would with any other library.
Related: D3.js for Presentations: Static Charts vs. Shareable Pages
Continue the workflow
Explore related guides
Next step
Move from insight to a stakeholder-ready story.
Infigure helps teams replace the export-to-slides loop with one connected reporting workflow for analysis, narrative, and delivery.