| name | marimo-notebook-dev |
| description | Create, edit, run, and convert marimo reactive Python notebooks. Use when working with .py marimo notebook files, creating reactive/interactive notebooks, exploratory data analysis in notebook format, or converting from Jupyter (.ipynb) to marimo. Marimo notebooks are pure Python files with @app.cell decorators that enable reactivity. Also covers wigglystuff widgets for advanced interactivity. |
Marimo Notebooks
Marimo notebooks are reactive Python notebooks stored as pure .py files. Cells auto-execute when dependencies change, modeled as a directed acyclic graph (DAG).
Notebook Structure
import marimo
__generated_with = "0.10.0"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _(mo):
mo.md("# Hello")
return
if __name__ == "__main__":
app.run()
Key rules:
- Each cell is a function decorated with
@app.cell - Variables shared by returning tuples:
return (var1, var2,) - Cells receive variables as parameters:
def _(mo, df): - Execution order follows dependency graph, not position
CLI Commands
marimo new # Create new notebook
marimo new "build a dashboard" # AI-generated starter
marimo edit notebook.py # Open editor
marimo run notebook.py # Run as app (code hidden)
marimo run notebook.py --include-code
python notebook.py # Run as script
python notebook.py -- --arg val
# Convert
marimo convert notebook.ipynb -o notebook.py
marimo convert notebook.md -o notebook.py
# Export
marimo export html notebook.py -o out.html
marimo export html-wasm notebook.py -o out.html # Interactive WASM
marimo export ipynb notebook.py -o out.ipynb
marimo export script notebook.py -o out.py
marimo export md notebook.py -o out.md
# Utilities
marimo check notebook.py --fix # Lint and format
marimo tutorial intro # Interactive tutorials
SQL Integration
SQL cells integrate seamlessly with Python. Results become dataframes.
@app.cell
def _(mo):
# Query Python dataframes directly
result = mo.sql(f"""
SELECT * FROM df
WHERE value > {threshold.value}
LIMIT 100
""")
return (result,)
SQL can query:
- Python dataframes (polars/pandas) by variable name
- DuckDB, PostgreSQL, MySQL, SQLite
- Snowflake, BigQuery, Redshift
- CSV/Parquet files, S3
Use {{}} to escape literal braces in SQL.
State Management
UI elements have built-in state via .value. For advanced cases use mo.state:
@app.cell
def _(mo):
get_count, set_count = mo.state(0)
return get_count, set_count
@app.cell
def _(mo, get_count, set_count):
mo.ui.button(label=f"Count: {get_count()}", on_click=lambda _: set_count(lambda n: n + 1))
return
When setter is called, all cells referencing the getter re-run. Use sparingly—prefer reactive UI elements.
Caching
@app.cell
def _(mo):
@mo.cache
def expensive_computation(x):
# Cached across cell re-runs
return x ** 2
return (expensive_computation,)
mo.cache persists across re-runs unlike functools.cache.
Control Flow
@app.cell
def _(mo, data):
mo.stop(data is None, mo.md("*Upload data first*"))
# Only runs if data exists
process(data)
return
Wigglystuff Widgets
Creative anywidgets for advanced interactivity. Install: uv pip install wigglystuff
from wigglystuff import Slider2D, Paint, SortableList, Matrix
import marimo as mo
# 2D slider
slider2d = mo.ui.anywidget(Slider2D())
slider2d.x, slider2d.y # Get coordinates
# Drawing canvas
paint = mo.ui.anywidget(Paint(width=400, height=300))
paint.to_pil() # Get as PIL image
# Drag-and-drop list
sortable = mo.ui.anywidget(SortableList(items=["A", "B", "C"]))
sortable.items # Current order
# Matrix input
matrix = mo.ui.anywidget(Matrix(rows=3, cols=3))
matrix.data # Nested list of values
Available widgets: Slider2D, Matrix, Paint, EdgeDraw, SortableList, ColorPicker, KeystrokeWidget, GamepadWidget, WebkitSpeechToTextWidget, CopyToClipboard, WebcamCapture, CellTour
See references/wigglystuff.md for complete widget reference.
Common Patterns
Reactive UI with data:
@app.cell
def _(mo):
dropdown = mo.ui.dropdown(["2020", "2021", "2022"], label="Year")
dropdown
return (dropdown,)
@app.cell
def _(pl, dropdown):
df = pl.read_parquet(f"data/{dropdown.value}.parquet")
return (df,)
@app.cell
def _(df, mo):
mo.ui.table(df)
return
Interactive chart selection:
@app.cell
def _(mo, alt, df):
chart = alt.Chart(df).mark_point().encode(x="x", y="y")
selection = mo.ui.altair_chart(chart)
selection
return (selection,)
@app.cell
def _(selection):
selected_points = selection.value # DataFrame of selected points
return
Form with validation:
@app.cell
def _(mo):
form = mo.ui.form(
mo.md("""
**Name**: {name}
**Email**: {email}
""").batch(
name=mo.ui.text(placeholder="Your name"),
email=mo.ui.text(placeholder="email@example.com")
)
)
form
return (form,)
@app.cell
def _(form, mo):
mo.stop(not form.value, mo.md("*Submit the form*"))
mo.md(f"Hello, {form.value['name']}!")
return
Lazy execution mode:
app = marimo.App(width="medium")
# In settings, enable lazy execution
# Cells mark as stale instead of auto-running
Resources
- UI components: See
references/ui_components.md - Wigglystuff: See
references/wigglystuff.md - Templates: See
assets/for starter notebooks - Conversion: See
scripts/convert_notebook.py