Get started
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 marimo-export
Install Python 3.10 through 3.14 and 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.
Check the installation:
uv run marimo-export doctorCreate the notebook
Create report.py:
import marimo
__generated_with = "0.24.0"
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.
Declare two states and two outputs
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 |
Build and verify the export
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.
You should see:
Verified 2 assets and 923 B for 2 statesThe byte count can change when the supported marimo snapshot encoding changes. The stable result is two states with two named outputs each.
The static application below reads the same generated export. Switch between the two states to see the JSON summary and rendered report change together.
Inspect the files
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.jsonsummary stays inline in index.json. Each state produces a distinct report snapshot, so each report has its own asset.
Read the monthly state
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'}
Trueopen_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.