Skip to content
Feeding a clanker? Grab this page as raw .md

ObservablePlot API#

Bases: AnyWidget

Run Observable Plot <https://observablehq.com/plot/>_ code in a notebook.

Observable Plot is a JavaScript charting library. This widget is a thin runner: it loads Plot (and d3) from a CDN, then evaluates a blob of JavaScript you supply (the code traitlet) exactly as you would write it in an Observable notebook — a bare Plot.plot({...}) expression that returns a DOM node. The returned node is mounted inline; Python's job is to hand over the source, the data, and the sizing.

Python values are injected into the code's scope by name via variables: each key becomes an in-scope JavaScript variable holding the (JSON-ified) value. So variables={"vacancies": df} lets your code reference vacancies directly::

Plot.plot({
    marks: [
        Plot.barY(vacancies, { x: "month", y: "vacancies" }),
        Plot.ruleY([0]),
    ],
})

Plot and d3 are always in scope, along with container (the target DOM element), the width / height ints, and the widget model. Pandas / polars DataFrames and numpy arrays in variables are converted to plain JSON structures (records / lists) before syncing.

The code is evaluated verbatim in the browser, so only pass JavaScript you trust (typically your own).

Reassigning code, variables, width, height, or version re-runs the code and rebuilds the chart. Observable Plot has no incremental redraw — each run produces a fresh SVG — but the new node is swapped in atomically (the previous chart stays put until it is ready), so updates driven by e.g. a slider do not flash a blank frame.

Examples:

import marimo as mo
import pandas as pd
from wigglystuff import ObservablePlot

df = pd.DataFrame({"month": ["Jan", "Feb", "Mar"], "vacancies": [3, 5, 2]})
code = '''
    Plot.plot({
        marks: [
            Plot.barY(vacancies, { x: "month", y: "vacancies" }),
            Plot.ruleY([0]),
        ],
    })
'''
mo.ui.anywidget(ObservablePlot(code, variables={"vacancies": df}))

Point at a local file or a URL instead of an inline string — the first argument accepts any of the three forms:

ObservablePlot("charts/bars.js", variables={"vacancies": df})
ObservablePlot("https://example.com/chart.js")

Create an ObservablePlot widget.

The plot source is resolved in Python, so the browser always receives plain JavaScript. code (or src) may be any of:

  • an inline JavaScript string,
  • a path to a local .js file, or
  • an http(s):// URL.

URLs and file paths are fetched/read here at construction time; the detection is by value, so ObservablePlot("https://.../chart.js") and ObservablePlot("Plot.plot({...})") both do the right thing.

Parameters:

Name Type Description Default
code Optional[str]

Inline JavaScript, a local file path, or an http(s):// URL. Mutually exclusive with src.

None
src Optional[str]

An explicit alias for code (same accepted forms), handy when the intent is "load from here". Mutually exclusive with code.

None
variables Optional[Dict[str, Any]]

Mapping of name -> value injected into the code's scope as JavaScript variables. DataFrames and numpy arrays are converted to JSON-serializable structures.

None
width int

Container width in pixels.

500
height int

Container height in pixels.

300
version str

The @observablehq/plot version to load from the CDN. Defaults to "latest"; pin an exact version (e.g. "0.6.17") for reproducibility.

'latest'
**kwargs Any

Forwarded to anywidget.AnyWidget.

{}

Raises:

Type Description
ValueError

If neither or both of code and src are given.

Source code in wigglystuff/observable_plot.py
def __init__(
    self,
    code: Optional[str] = None,
    *,
    src: Optional[str] = None,
    variables: Optional[Dict[str, Any]] = None,
    width: int = 500,
    height: int = 300,
    version: str = "latest",
    **kwargs: Any,
) -> None:
    """Create an ObservablePlot widget.

    The plot source is resolved in Python, so the browser always receives
    plain JavaScript. ``code`` (or ``src``) may be any of:

    * an inline JavaScript string,
    * a path to a local ``.js`` file, or
    * an ``http(s)://`` URL.

    URLs and file paths are fetched/read here at construction time; the
    detection is by value, so ``ObservablePlot("https://.../chart.js")`` and
    ``ObservablePlot("Plot.plot({...})")`` both do the right thing.

    Args:
        code: Inline JavaScript, a local file path, or an ``http(s)://``
            URL. Mutually exclusive with ``src``.
        src: An explicit alias for ``code`` (same accepted forms), handy
            when the intent is "load from here". Mutually exclusive with
            ``code``.
        variables: Mapping of name -> value injected into the code's scope
            as JavaScript variables. DataFrames and numpy arrays are
            converted to JSON-serializable structures.
        width: Container width in pixels.
        height: Container height in pixels.
        version: The `@observablehq/plot` version to load from the CDN.
            Defaults to ``"latest"``; pin an exact version (e.g. ``"0.6.17"``)
            for reproducibility.
        **kwargs: Forwarded to ``anywidget.AnyWidget``.

    Raises:
        ValueError: If neither or both of ``code`` and ``src`` are given.
    """
    if (code is None) == (src is None):
        raise ValueError("Provide exactly one of `code` or `src`.")

    source = code if code is not None else src
    super().__init__(
        code=self._resolve_source(source),
        variables=variables if variables is not None else {},
        width=width,
        height=height,
        version=version,
        **kwargs,
    )

Synced traitlets#

Traitlet Type Notes
code str Resolved Observable Plot JavaScript (inline JS, or the contents of a file / URL, fetched in Python), evaluated as an expression that returns the DOM node to mount.
variables dict Name → value mapping injected into the code's scope as JavaScript variables. DataFrames and numpy arrays are converted to JSON records/lists on assignment.
width int Container width in pixels.
height int Container height in pixels.
version str @observablehq/plot version loaded from the CDN (defaults to "latest").
error str Read-back of the latest JS runtime / CDN-load error, or "".