The Problem With Jupyter Notebooks
Jupyter notebooks have one fundamental flaw: hidden state. You can run cells out of order, delete a cell but keep its variables in memory, and share a notebook that works on your machine but fails for everyone else because they ran cells in a different order.
The fix requires a different mental model. Marimo approaches notebooks as a directed acyclic graph (DAG) — cells have explicit dependencies, and the execution order is always deterministic.
How Marimo's Reactivity Works
# Cell 1: define a variable
df = pd.read_csv("sales.csv")
# Cell 2: depends on df — runs automatically when Cell 1 changes
summary = df.groupby("region")["revenue"].sum()
# Cell 3: depends on summary — runs automatically when Cell 2 changes
chart = summary.plot(kind="bar")
Edit Cell 1 (load a different CSV) → Cell 2 and Cell 3 re-run automatically. This is the DAG. Marimo tracks dependencies at the variable level, not the cell level.
Interactive UI Without Widget Overhead
import marimo as mo
import polars as pl
# Create an interactive slider
threshold = mo.ui.slider(0, 100, value=50, label="Revenue threshold")
threshold # display it
# This cell re-runs when threshold changes
df = pl.read_csv("data.csv")
filtered = df.filter(pl.col("revenue") > threshold.value)
mo.ui.table(filtered)
Marimo UI elements (mo.ui.*) are reactive — change a slider and all cells that reference its value re-run. This works without IPython widgets or JavaScript.
Notebooks as Pure Python Files
This is a significant advantage for teams. Every Marimo notebook is a valid .py file:
import marimo
app = marimo.App()
@app.cell
def load_data():
import pandas as pd
df = pd.read_csv("sales.csv")
return (df,)
@app.cell
def analyze(df):
summary = df.groupby("region")["revenue"].sum()
return (summary,)
if __name__ == "__main__":
app.run()
This means:
- Git diffs are readable (no JSON notebook format)
- Import notebooks as Python modules
- Run in CI without a notebook server
- Linters and type checkers work on them
Converting a Notebook to a Web App
# Development mode (edit and run interactively)
marimo edit my_notebook.py
# App mode (read-only, deployable web app)
marimo run my_notebook.py
# Convert existing Jupyter notebook
marimo convert my_notebook.ipynb > my_notebook.py
marimo run serves the notebook as a web app where users interact with UI elements but cannot edit cells. This is the equivalent of Streamlit for Marimo notebooks.
Marimo vs Jupyter vs Observable
| | Jupyter | Marimo | Observable | |---|---|---|---| | Reactivity | Manual | Automatic DAG | Automatic | | File format | JSON | Python | JavaScript | | Version control | Difficult | Easy (plain .py) | Easy | | Type checking | No | Yes | No | | Deploy as app | nbconvert/Voilà | marimo run | Observable |
When to Use Marimo
Marimo is best for: reproducible research notebooks, data exploration that becomes a report, internal tools that non-technical users will interact with, and any team that has suffered from Jupyter hidden-state bugs. Keep Jupyter for: existing notebooks in ecosystems that depend on the .ipynb format, or when colleagues are not ready to change their workflow.