# marimo: a reactive Python notebook stored as plain .py files

> marimo replaces Jupyter's manual re-run model with a dependency graph over cell variables, and saves notebooks as importable Python. The install is one pip command; the trade-off is that the reactivity model has to be learned before it stops surprising you.

**marimo-team/marimo** — A reactive notebook for Python : run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.

- Repository: https://github.com/marimo-team/marimo
- Website: https://marimo.io
- Stars: 22,941 · Forks: 1,301
- Language: Python
- License: Apache-2.0
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/marimo-team-marimo

## The problem marimo solves: hidden state in Jupyter notebooks

A traditional Jupyter notebook executes cells in whatever order you clicked them. Delete a cell, and its variables stay in the kernel. Re-run an early cell after editing it, and later cells keep outputs computed from the old value. The notebook file records execution counts, so a diff between two versions is dominated by output blobs rather than code.

marimo's README frames the fix as a guarantee: "marimo guarantees your notebook code, outputs, and program state are consistent." It does that by treating the notebook as a dependency graph rather than a script. Run a cell and marimo runs the cells that reference its variables; delete a cell and marimo scrubs its variables from memory. The README calls this "no hidden state" and pairs it with deterministic execution.

The intended audience is narrow enough to state plainly: people who write Python to explore data, query warehouses, and then hand the result to someone else. That includes research engineers, analysts who write code, and anyone who has shipped a notebook where the outputs no longer match the code above them.

## How the reactive execution model actually works

Each marimo cell is a unit whose inputs and outputs are its global variable references. When a cell runs, marimo records which variables it defines and which it reads, then re-executes the downstream cells that consume those variables. This is why the README can promise that interacting with a slider, dropdown, or dataframe transformer re-runs the cells using it: the UI element is just a variable with a value.

The reactivity is not unconditional. marimo lets you configure the runtime to be lazy, in which case affected cells are marked stale instead of executed. That matters for anything expensive, and the README is explicit that this exists to prevent accidental execution of costly cells while still giving guarantees about program state. The default is eager execution, so the lazy mode is a deliberate switch you make per notebook, not a global setting you inherit.

Two consequences follow from the graph model. First, a cell cannot mutate a variable defined in another cell; that would break the graph, and the documentation's reactivity guide is the place to check the exact rules before you restructure an existing notebook. Second, the notebook file itself is plain Python. The repository's pyproject.toml describes the package as "A library for making reactive notebooks and apps," and the README states notebooks are stored as .py files, which is what makes git diffs readable and what lets a notebook be imported as a module.

## Installing marimo and running a first notebook

The README gives a single install-and-tutorial line. It installs the package from PyPI and immediately opens an interactive tutorial notebook, which is the fastest way to see the reactive behavior before you commit to it.

```bash
pip install marimo && marimo tutorial intro
```

If you prefer a clean environment, the project also publishes to conda-forge under the name marimo, so `conda install -c conda-forge marimo` is the equivalent path. The README does not give a version pin, and pyproject.toml keeps dependencies deliberately loose, so the installed version is whatever your resolver picks.

To start an empty notebook rather than the tutorial, open the editor on a file. marimo creates the .py file if it does not exist and opens the editor in your browser.

```bash
marimo edit
```

From there, write a cell that defines a variable and a second cell that uses it. Editing the first cell and re-running it should re-run the second without you touching it; that is the behavior to watch for. If you want the notebook to run as a script, the README states it is executable as a Python script and can be parameterized by CLI args, and the repository's examples/running_as_a_script/ directory is where the project keeps its own examples of that.

## Where the reactive model gets in the way

The failure mode is order dependence that the graph cannot express. A notebook that trains a model in one cell, logs metrics in another, and appends to a file in a third is a sequence of side effects, not a function. marimo will happily run those cells, but the guarantee it advertises applies to variables, not to files, network calls, or database writes. Re-running a downstream cell does not undo a write that already happened.

The second constraint is that you cannot freely reassign across cells. In Jupyter, defining `x` in cell 1 and redefining it in cell 5 is a common (if confusing) habit. Under a dependency graph, that is a cycle or an ambiguity, and the documentation's reactivity guide is the place to confirm how marimo resolves it before you port a large notebook over.

Third, the lazy runtime is a per-notebook decision with a real cost. If you enable it, cells that would have re-run now sit stale until you run them, which means the reader of a shared notebook cannot assume the outputs on screen reflect the current code. That is a better failure mode than silent staleness, but it is still a failure mode.

Finally, tooling. The README lists VS Code and Cursor support through a marketplace extension, PyCharm through a plugin, and editing from neovim, Zed, or any text editor through a watch mode. Anything outside that list, including Jupyter-specific extensions and IPython magics, is not covered by the README, and a notebook that depends on them is a migration project rather than a file rename.

## marimo compared with Jupyter and Streamlit

The README positions marimo as replacing jupyter, streamlit, jupytext, ipywidgets, and papermill. The difference from Jupyter is the execution model and the file format. Jupyter stores .ipynb JSON with outputs and execution counts and leaves execution order to you; marimo stores .py and derives execution order from variable dependencies. That single change is what makes the git story work, and it is also what makes porting an order-dependent notebook non-trivial.

The difference from Streamlit is the direction of the artifact. Streamlit apps are scripts you run as a server and share as an app; marimo notebooks are developed interactively and can additionally be deployed as an app or as slides. The README also mentions running in the browser via WASM, which the repository supports with a pyodide/ directory at the top level. If your goal is a dashboard for non-technical users, Streamlit's model is simpler; if your goal is an analysis that a colleague can open, read, and re-run, marimo's is closer.

The bundled SQL engine is the third differentiator worth naming. The README states you can build SQL queries that depend on Python values and execute them against dataframes, databases, lakehouses, CSVs, or Google Sheets, with results returned as a Python dataframe, and that the notebook remains pure Python even when it uses SQL. The examples/sql/ directory holds the project's own examples. DuckDB or SQLAlchemy users will want to check that path against their existing connection setup rather than assume it.

## Licence, maintenance and upgrade cost

marimo is licensed Apache-2.0, which permits commercial use and modification with the usual notice and patent-grant conditions. That is a permissive licence, and it is the same one used by much of the Python data stack. This is not legal advice; if you redistribute marimo inside a product, read the LICENSE file in the repository rather than this paragraph.

The repository is not archived, and the last push was on 2026-08-17, the same date as the 0.24.0 release. Preceding releases 0.23.16 and 0.23.15 landed on 2026-07-31 and 2026-07-23, so the release cadence visible in the repository is roughly every one to three weeks. The version in pyproject.toml is 0.24.2, ahead of the most recent release listed, which is normal for a main branch between tags.

Upgrade cost is the part worth budgeting for. The project is pre-1.0, so minor version bumps can change behavior. The dependency list in pyproject.toml is deliberately loose, with lower bounds and comments explaining upper bounds such as the one on jedi, and the file states the maintainers keep dependencies minimal to avoid conflicts with user environments. That reduces resolution pain but means your environment, not marimo, decides most transitive versions. Notebooks themselves are plain .py, so a bad upgrade is a git revert rather than a data migration, which is the strongest argument for the format.

## Conclusion

Adopt marimo if your team already fights stale Jupyter state, needs notebooks that diff cleanly in git, or wants the same .py file to run as a script or serve as an app. Do not adopt it if your notebook is a long, order-dependent sequence of side-effecting cells, or if you depend on Jupyter-only extensions and magic commands; the reactive model assumes each cell is a function of its inputs. Before committing, verify three things on your own data: that your heavy cells behave under the lazy runtime configuration, that your SQL connections work through the built-in engine, and that importing a notebook's functions into another notebook matches how your team shares code today.

## FAQ

### How do I install marimo?

The README gives a single command: pip install marimo && marimo tutorial intro. The package is also published to conda-forge under the name marimo, so conda install -c conda-forge marimo is the equivalent path.

### How do I use marimo in VS Code?

The README states you can run marimo in VS Code or Cursor through the marimo-team.vscode-marimo marketplace extension, and in PyCharm through a JetBrains plugin. Editing from neovim, Zed, or any other text editor is supported through a watch mode described in the docs.

### How do I use the marimo notebook?

Run marimo edit to open the editor, then write a cell that defines a variable and a second cell that uses it. Run a cell and marimo re-runs the cells that reference its variables, or marks them stale if the runtime is configured to be lazy.

### How do I use marimo with Python?

marimo notebooks are stored as pure Python .py files, and the README states they can be executed as a Python script parameterized by CLI args. The repository keeps examples of that under examples/running_as_a_script/.

## Sources

- [Official documentation](https://marimo.io)
- [Official README](https://github.com/marimo-team/marimo#readme)
- [Project repository](https://github.com/marimo-team/marimo)
- [Release notes](https://github.com/marimo-team/marimo/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/marimo-team-marimo
