| title | Get started |
|---|---|
| description | Create two deterministic notebook states, publish JSON and rendered output, verify the files, and read one state. |
Create a local marimo reactive Python notebook, run it for
weekly and monthly, publish a JSON summary and rendered report for each
state, then verify and read the written notebook export.
If you are still choosing where Python should run, read When to use marimo-export first.
Install a Python version accepted by the package metadata, plus uv. Continuous integration tests each supported version. The package metadata pins its supported marimo release.
Create an empty project:
mkdir notebook-export-demo
cd notebook-export-demo
uv init --bare --no-workspace
uv add marimo-exportThe installation downloads packages from the Python package registry when they are absent from the local uv cache.
To test unreleased changes, use a preview build.
Check the installation:
uv run marimo-export doctorCreate report.py:
import marimo
app = marimo.App()
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _(mo):
days = mo.ui.slider(1, 30, value=7, label="Days")
days
return (days,)
@app.cell
def _(days):
summary = {"days": days.value, "label": f"Last {days.value} days"}
summary
return (summary,)
@app.cell
def _(days, mo, summary):
report = mo.md(f"## {summary['label']}\n\nSelected window: **{days.value} days**")
report
return (report,)
if __name__ == "__main__":
app.run()The slider starts at 7 days. Each following cell shows one value: days is
the input, summary is structured data, and report is rendered notebook
output.
Create report.export.yaml:
schema: marimo-export.spec.v2
default_state: weekly
states:
weekly: {}
monthly:
days: 30
outputs:
summary:
source: { kind: json, selector: summary }
report:
source: { kind: output, selector: report }The empty weekly row keeps the slider's starting value. The monthly row uses
30. Both states publish the same output names:
| State | days |
Outputs |
|---|---|---|
weekly |
7 |
summary, report |
monthly |
30 |
summary, report |
Run:
mkdir -p dist
uv run marimo-export build report.py \
--spec report.export.yaml \
--output dist/report
uv run marimo-export verify dist/reportbuild runs the notebook as a Python program with your file, credential,
package, and network access. Review the notebook before running it.
build runs both states, writes the notebook export, and verifies index.json
and both report assets.
verify prints:
Verified 2 assets and 923 B for 2 states
The byte count can change when the supported marimo snapshot encoding changes. The stable result is two states with two named outputs each.
The quickstart application reads the same generated export. Switch between the two states to see the JSON summary and rendered report change together.
The written directory has one entry point and two asset files named by their content hashes:
dist/report/
index.json
assets/
<sha256>.output.json
<sha256>.output.json
summary stays inline in index.json. Each state produces a distinct report
snapshot, so each report has its own asset.
Create read_report.py:
from marimo_export import open_export
notebook_export = open_export("dist/report")
monthly = notebook_export.state("monthly")
summary = monthly.output("summary")
report = monthly.output("report")
print(dict(monthly.inputs))
print(dict(summary.json()))
print(len(report.asset_bytes()) > 0)Run it:
uv run python read_report.pyExpected output:
{'days': 30}
{'days': 30, 'label': 'Last 30 days'}
True
open_export() validates canonical index.json. json() reads the inline
summary. asset_bytes() reads and verifies the selected report asset.
Related: Overview explains the concepts. Browser applications covers the TypeScript reader. Build and capture covers planning, live sessions, progress, cancellation, and replacement behavior.