Skip to main content

Reactivity

marimo reacts to your code changes: run a cell, and all other cells that reference the variables it defines are automatically run with the latest data. This keeps your code and outputs consistent, and eliminates bugs before they happen.
Reactivity is one of the biggest differences between marimo and traditional notebooks like Jupyter. It eliminates hidden state and provides a deterministic execution order.

How Reactivity Works

marimo statically analyzes each cell to determine:
  • References: The global variables it reads but doesn’t define
  • Definitions: The global variables it defines
It then forms a directed acyclic graph (DAG) on cells, with an edge from one cell to another if the latter references any of the definitions of the former.
1

Write code in a cell

Define a variable in a cell:
x = mo.ui.slider(1, 9)
x
2

Reference it in another cell

Reference that variable in another cell:
mo.md(f"$e^{x.value} = {math.exp(x.value):0.3f}$")
3

Automatic updates

When you interact with the slider, marimo automatically re-runs the markdown cell with the new value!
Runtime Rule: When a cell is run, marimo automatically runs all other cells that reference any of the global variables it defines.

Execution Order

The order cells are executed in is determined by the relationships between cells and their variables, not by the order of cells on the page (similar to a spreadsheet). This lets you organize your code in whatever way makes the most sense to you. For example, you can put helper functions at the bottom of your notebook.
# Cell 1: Use the function
result = process_data(df)
mo.ui.table(result)
# Cell 2: Define the function (can be at the bottom)
def process_data(data):
    return data.groupby('category').sum()

Global Variable Names Must Be Unique

marimo requires that every global variable be defined by only one cell. This lets marimo keep code and outputs consistent.
A variable can refer to any Python object. Functions, classes, and imported names are all variables.

Creating Local Variables

Variables prefixed with an underscore (e.g., _x) are “local” to a cell: they can’t be read by other cells. Multiple cells can reuse the same local variable names.
# Cell 1
_temp = process_step_1(data)
result_1 = finalize(_temp)
# Cell 2 - can reuse _temp
_temp = process_step_2(data)
result_2 = finalize(_temp)

Encapsulating Code in Functions

If you want most or all the variables in a cell to be temporary, encapsulate them in a function:
def _():
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    ax.plot([1, 2])
    return ax

_()
Here, the variables plt, fig, and ax aren’t added to the globals.

Variable Mutations Are Not Tracked

marimo does not track mutations to objects. Mutations like my_list.append(42) or my_object.value = 42 don’t trigger reactive re-runs of other cells.
Avoid defining a variable in one cell and mutating it in another.
# Cell 1
l = [1]
# Cell 1
l = [1]

Why Not Track Mutations?

Tracking mutations reliably is impossible in Python. Reacting to mutations could result in surprising re-runs of notebook cells. If you need to mutate a variable (such as adding a new column to a dataframe), perform the mutation in the same cell as the one that defines it:
# Good: mutation in the same cell as definition
df = pd.DataFrame({"my_column": [1, 2]})
df["another_column"] = [3, 4]

Deleting Cells Deletes Variables

In marimo, deleting a cell deletes its global variables from program memory. Cells that previously referenced these variables are automatically re-run and invalidated. This eliminates a common cause of bugs in traditional notebooks like Jupyter.

Lazy Mode for Expensive Notebooks

For notebooks where cells might take a long time to run or have side-effects, you can configure the runtime to be lazy instead of automatic.
marimo provides several tools for working with expensive notebooks:
  • Configure the runtime to be lazy, marking cells as stale instead of running them automatically
  • Use mo.stop() to conditionally stop execution at runtime
  • Disable specific cells to prevent them from running
Access runtime configuration through the notebook settings menu.

Disabling Cells

You can disable cells to block them and their dependents from running. This is useful when you want to edit one part of a notebook without triggering automatic execution of expensive dependent cells. When you re-enable a cell, if any of the cell’s ancestors ran while it was disabled, marimo will automatically run it.

Visualizing the Dependency Graph

marimo provides several tools to help you understand and navigate the dependency graph:

Graph View

Visualize the complete dependency graph of your notebook

Minimap

See a bird’s-eye view of your notebook structure

Reference Highlighting

Automatically highlights related cells as you navigate

Runtime Configuration

Through the notebook settings menu, you can configure:
  • Autorun on startup: Whether to run cells automatically when opening the notebook
  • Autorun on cell execution: Whether to run dependent cells automatically
  • Module autoreloader: Automatically reload imported modules when they change
  • Lazy execution: Mark cells as stale instead of running them automatically
The runtime configuration allows you to balance between automatic updates and manual control, depending on your workflow.

Build docs developers (and LLMs) love