Skip to content

State

Stop! Read the interactivity guide first!

Read the guide on creating interactive elements before reading this one!

Advanced topic!

This guide covers reactive state (mo.state), an advanced topic.

You likely don't need mo.state. UI elements already have built-in state, their associated value, which you can access with their value attribute. For example, mo.ui.slider() has a value that is its current position on an interval, while mo.ui.button() has a value that can be configured to count the number of times it has been clicked, or to toggle between True and False. Additionally, interacting with UI elements bound to global variables automatically executes cells that reference those variables, letting you react to changes by just reading their value attributes. This functional paradigm is the preferred way of reacting to UI interactions in marimo. Chances are, the reactive execution built into UI elements will suffice. (For example, you don't need reactive state to handle a button click.)

That said, here are some signs you might need mo.state:

  • you need to maintain historical state related to a UI element that can't be computed from its built-in value (e.g., all values the user has ever input into a form)
  • you need to synchronize two different UI elements (e.g., so that interacting with either one controls the other)
  • you need to introduce cycles across cells

In over 99% of cases, you don't need and shouldn't use mo.state. This feature can introduce hard-to-find bugs.

marimo.state

state(
    value: T, allow_self_loops: bool = False
) -> tuple[State[T], Callable[[T], None]]

Mutable reactive state.

Warning: reactive state is an advanced feature that you likely don't need; it makes it possible to introduce cycles and hard-to-debug code execution paths. In almost all cases, you should prefer using marimo's built-in reactive execution and interactivity.

This function takes an initial value and returns:

  • a getter function that reads the state value
  • a setter function to set the state's value

When you call the setter function and update the state value in one cell, all other cells that read any global variables assigned to the getter will automatically run. By default, the cell that called the setter function won't be re-run, even if it references the getter. To allow a state setter to possibly run the caller cell, set the keyword argument allow_self_loops=True.

You can use this function with UIElement on_change handlers to trigger side-effects when an element's value is updated; however, you should prefer using marimo's built-in reactive execution for interactive elements.

For example, you can tie multiple UI elements to derive their values from shared state.

Examples:

Create state:

get_count, set_count = mo.state(0)
Read the value:
get_count()
Update the state:
set_count(1)
Update based on current value:
set_count(lambda value: value + 1)

Never mutate the state directly. You should only change its value through its setter.

Synchronizing multiple UI elements:

get_state, set_state = mo.state(0)
# Updating the state through the slider will recreate the number (below)
slider = mo.ui.slider(0, 100, value=get_state(), on_change=set_state)
# Updating the state through the number will recreate the slider (above)
number = mo.ui.number(0, 100, value=get_state(), on_change=set_state)
# slider and number are synchronized to have the same value (try it!)
[slider, number]

Warning

Do not store marimo.ui elements in state; doing so can cause hard-to-diagnose bugs.

PARAMETER DESCRIPTION
value

Initial value of the state.

TYPE: T

allow_self_loops

If True, a cell that calls a state setter and also references its getter will be re-run. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
State[T]

A tuple of (getter function, setter function). The getter function

Callable[[T], None]

retrieves the state value; the setter function takes a new value or a

tuple[State[T], Callable[[T], None]]

function that updates the current value.