One of marimo’s most powerful features is its first-class support for interactive user interface (UI) elements: interacting with a UI element will automatically run cells that reference it. ## marimo.ui
The marimo.ui module has a library of pre-built elements.For example, here's a slider: slider%20%3D%20mo.ui.slider(start%3D1%2C%20stop%3D10%2C%20step%3D1)%0Aslider%0A%0Amo.md(%0A%20%20%20%20f%22%22%22%0A%20%20%20%20The%20%60marimo.ui%60%20module%20has%20a%20library%20of%20pre-built%20elements.%0A%0A%20%20%20%20For%20example%2C%20here's%20a%20%60slider%60%3A%20%7Bslider%7D%0A%20%20%20%20%22%22%22%0A)and here's its value: 1.mo.md(f%22and%20here's%20its%20value%3A%20**%7Bslider.value%7D**.%22)
How interactions run cells
Whenever you interact with a UI element, its value is sent back to Python. When this happens, all cells that reference the global variable bound to the UI element, but don’t define it, will run.
This simple rule lets you use UI elements to drive the execution of your program, letting you build interactive notebooks and tools for yourselves and others.
Interacting with a displayed UI element will only
trigger reactive execution if the UI element is assigned
to a global variable.
Every UI element has a value attribute that you can access in
Python.
You can embed UI elements in markdown using f-strings.For example, we can render the slider here:
marimo has a large library of simple UI elements: mo.md(%0A%20%20%20%20f%22%22%22%0A%20%20%20%20marimo%20has%20a%20large%20library%20of%20simple%20UI%20elements%3A%20%7Bbasic_ui_elements%7D%0A%20%20%20%20%22%22%22%0A)selected_element%20%3D%20construct_element(basic_ui_elements.value)%0Ashow_element(selected_element)value(selected_element)documentation(basic_ui_elements.value)
Composite elements
Composite elements are advanced elements
let you build UI elements out of other UI elements.
Use these powerful elements to logically group together related elements,
create a dynamic set of UI elements, or reduce the number of global
variables in your program.Select a composite element: mo.md(%0A%20%20%20%20f%22%22%22%23%23%23%20Composite%20elements%0A%0A%20%20%20%20Composite%20elements%20are%20advanced%20elements%0A%20%20%20%20let%20you%20build%20UI%20elements%20out%20of%20other%20UI%20elements.%0A%20%20%20%20Use%20these%20powerful%20elements%20to%20logically%20group%20together%20related%20elements%2C%0A%20%20%20%20create%20a%20dynamic%20set%20of%20UI%20elements%2C%20or%20reduce%20the%20number%20of%20global%0A%20%20%20%20variables%20in%20your%20program.%0A%0A%20%20%20%20Select%20a%20composite%20element%3A%20%7Bcomposite_elements%7D%0A%20%20%20%20%22%22%22%0A)composite_element%20%3D%20construct_element(composite_elements.value)%0Ashow_element(composite_element)value(composite_element)documentation(composite_elements.value)
You can build powerful interactive notebooks and apps using just mo.ui and reactivity.
Sometimes, however, you might want interactions to mutate state. Maybe you’re building a checklist, and you want to maintain a list of action items. Or maybe you want to tie two different UI elements, so that updating one updates the other.
For these and other cases, marimo provides the function mo.state, which creates state returns a getter function and a setter function. When you call the setter function in one cell, all other cells that reference the getter via a global variable are automatically run (similar to UI elements).
Mutable reactive stateThis 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, use the keyword argument
allow_self_loops=True.You can use this function in conjunction with UIElementon_change
handlers to trigger side-effects when an element's value is updated. For
example, you can tie multiple UI elements to derive their values from
shared state.Basic Usage.Create state:
get_count,set_count=mo.state(0)
Read the value:
get_count()
Update the state:
set_count(1)
Update the state based on the current value:
set_count(lambda value: value + 1)
Note: 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
lead to hard-to-diagnose bugs.Args:
value: initial value of the state
allow_self_loops: if True, if a cell calls a state setter
and also references its getter, the caller cell will be re-run;
defaults to False.
Returns:
getter function that retrieves the state value
setter function that takes a new value, or a function taking the current
value as its argument and returning a new value
mo.state takes an initial state value as its argument, and returns
a function that returns the state value;
a function that updates the state value.
For exaxmple,
get_counter, set_counter = mo.state(0)
get_counter%2C%20set_counter%20%3D%20mo.state(0)
Calling a state's setter function will only
trigger reactive execution if the corresponding getter is
assigned to and referenced via a global variable.
You can get far using just mo.ui, without state. That said,
judiciously using state can simplify the implementation of highly
interactive notebooks/apps, and also enables new use cases.
mo.accordion(%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22Tip%3A%20assign%20state%20getters%20to%20global%20variables%22%3A%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Calling%20a%20state's%20setter%20function%20will%20only%0A%20%20%20%20%20%20%20%20%20%20%20%20trigger%20reactive%20execution%20if%20the%20corresponding%20getter%20is%0A%20%20%20%20%20%20%20%20%20%20%20%20assigned%20to%20and%20referenced%20via%20a%20global%20variable.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22Tip%3A%20use%20state%20sparingly%22%3A%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20You%20can%20get%20far%20using%20just%20%60mo.ui%60%2C%20without%20state.%20That%20said%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20judiciously%20using%20state%20can%20simplify%20the%20implementation%20of%20highly%0A%20%20%20%20%20%20%20%20%20%20%20%20interactive%20notebooks%2Fapps%2C%20and%20also%20enables%20new%20use%20cases.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%7D%0A)Access the value of the state via its getter: get_counter()
returned 0mo.md(%0A%20%20%20%20f%22%22%22%0A%20%20%20%20Access%20the%20value%20of%20the%20state%20via%20its%20getter%3A%20%60get_counter()%60%0A%20%20%20%20returned%20**%7Bget_counter()%7D**%0A%20%20%20%20%22%22%22%0A)
Setting State
Set an element’s state by calling its setter function.
Call it with a new value: set_counter(1)
Call it with a function that takes the current value and returns a new one: set_counter(lambda count: count + 1)
State updates are reactive. When you call a state’s setter in one cell, all other cells that reference the state getter via a global variable are automatically run with the new state value. This is similar to how interacting with a UI element automatically runs all cells that use the element. The on_change callback. Every UI element takes an optional on_change callback, a function that takes the new value of the element and does anything with it. You can use the setter function in an on_change callback to mutate state.
🌊 Try it! Click the button below and watch what happens.
increment%20%3D%20mo.ui.button(%0A%20%20%20%20label%3D%22increment%22%2C%0A%20%20%20%20on_change%3Dlambda%20_%3A%20set_counter(lambda%20v%3A%20v%20%2B%201)%2C%0A)%0A%0Adecrement%20%3D%20mo.ui.button(%0A%20%20%20%20label%3D%22decrement%22%2C%0A%20%20%20%20on_change%3Dlambda%20_%3A%20set_counter(lambda%20v%3A%20v%20-%201)%2C%0A)%0A%0Amo.hstack(%5Bincrement%2C%20decrement%5D%2C%20justify%3D%22center%22)The counter's current value is 0!This cell runs automatically on button click, even though it
doesn't reference either button.mo.md(%0A%20%20%20%20f%22%22%22%0A%20%20%20%20The%20counter's%20current%20value%20is%20**%7Bget_counter()%7D**!%0A%0A%20%20%20%20This%20cell%20runs%20automatically%20on%20button%20click%2C%20even%20though%20it%0A%20%20%20%20doesn't%20reference%20either%20button.%0A%20%20%20%20%22%22%22%0A)
Calling a state's setter in one cell won't ever cause that same
cell to re-execute, even if it reads that state getter. This
prevents accidental infinite loops and makes some things, like
tying elements, easier."
The remaining cells are helper data structures and functions. You can look at their code if you’re curious how certain parts of this tutorial were implemented.
composite_elements%20%3D%20mo.ui.dropdown(%0A%20%20%20%20options%3Ddict(%0A%20%20%20%20%20%20%20%20sorted(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22array%22%3A%20mo.ui.array%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22batch%22%3A%20mo.ui.batch%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22dictionary%22%3A%20mo.ui.dictionary%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22form%22%3A%20mo.ui.form%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D.items()%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%2C%0A)basic_ui_elements%20%3D%20mo.ui.dropdown(%0A%20%20%20%20options%3Ddict(%0A%20%20%20%20%20%20%20%20sorted(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22button%22%3A%20mo.ui.button%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22checkbox%22%3A%20mo.ui.checkbox%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22date%22%3A%20mo.ui.date%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22dropdown%22%3A%20mo.ui.dropdown%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22file%22%3A%20mo.ui.file%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22multiselect%22%3A%20mo.ui.multiselect%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22number%22%3A%20mo.ui.number%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22radio%22%3A%20mo.ui.radio%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22range_slider%22%3A%20mo.ui.range_slider%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22slider%22%3A%20mo.ui.slider%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22switch%22%3A%20mo.ui.switch%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22tabs%22%3A%20mo.ui.tabs%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22table%22%3A%20mo.ui.table%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22text%22%3A%20mo.ui.text%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22text_area%22%3A%20mo.ui.text_area%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D.items()%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%2C%0A)def%20construct_element(value)%3A%0A%20%20%20%20if%20value%20%3D%3D%20mo.ui.array%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.array(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bmo.ui.text()%2C%20mo.ui.slider(1%2C%2010)%2C%20mo.ui.date()%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.batch%3A%0A%20%20%20%20%20%20%20%20return%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Name%3A%20%7Bname%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Date%3A%20%7Bdate%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20).batch(name%3Dmo.ui.text()%2C%20date%3Dmo.ui.date())%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.button%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.button(%0A%20%20%20%20%20%20%20%20%20%20%20%20value%3D0%2C%20label%3D%22click%20me%22%2C%20on_click%3Dlambda%20value%3A%20value%20%2B%201%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.checkbox%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.checkbox(label%3D%22check%20me%22)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.date%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.date()%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.dictionary%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.dictionary(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22slider%22%3A%20mo.ui.slider(1%2C%2010)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22text%22%3A%20mo.ui.text(%22type%20something!%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22array%22%3A%20mo.ui.array(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.ui.button(value%3D0%2C%20on_click%3Dlambda%20v%3A%20v%20%2B%201)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20_%20in%20range(3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label%3D%22buttons%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.dropdown%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.dropdown(%5B%22a%22%2C%20%22b%22%2C%20%22c%22%5D)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.file%3A%0A%20%20%20%20%20%20%20%20return%20%5Bmo.ui.file(kind%3D%22button%22)%2C%20mo.ui.file(kind%3D%22area%22)%5D%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.form%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.text_area(placeholder%3D%22...%22).form()%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.multiselect%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.multiselect(%5B%22a%22%2C%20%22b%22%2C%20%22c%22%5D)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.number%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.number(start%3D1%2C%20stop%3D10%2C%20step%3D0.5)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.radio%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.radio(%5B%22a%22%2C%20%22b%22%2C%20%22c%22%5D%2C%20value%3D%22a%22)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.range_slider%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.range_slider(start%3D1%2C%20stop%3D10%2C%20step%3D0.5)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.slider%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.slider(start%3D1%2C%20stop%3D10%2C%20step%3D0.5)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.switch%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.switch()%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.tabs%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.tabs(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Employee%20%231%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22first_name%22%3A%20%22Michael%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22last_name%22%3A%20%22Scott%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Employee%20%232%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22first_name%22%3A%20%22Dwight%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22last_name%22%3A%20%22Schrute%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.table%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.table(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%22first_name%22%3A%20%22Michael%22%2C%20%22last_name%22%3A%20%22Scott%22%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%22first_name%22%3A%20%22Dwight%22%2C%20%22last_name%22%3A%20%22Schrute%22%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20label%3D%22Employees%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.text%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.text()%0A%20%20%20%20elif%20value%20%3D%3D%20mo.ui.text_area%3A%0A%20%20%20%20%20%20%20%20return%20mo.ui.text_area()%0A%20%20%20%20return%20Nonedef%20show_element(element)%3A%0A%20%20%20%20if%20element%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20return%20mo.hstack(%5Belement%5D%2C%20%22center%22)def%20value(element)%3A%0A%20%20%20%20if%20element%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20v%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20element.value%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20isinstance(element%2C%20mo.ui.file)%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20element.name()%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20The%20element's%20current%20value%20is%20%7Bmo.as_html(element.value)%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)def%20documentation(element)%3A%0A%20%20%20%20if%20element%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20return%20mo.accordion(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Documentation%20on%20%60mo.ui.%7Belement.__name__%7D%60%22%3A%20mo.doc(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20element%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)from%20dataclasses%20import%20dataclassimport%20marimo%20as%20mo Back to top
---title: Uimarimo-version: 0.5.2format: htmlfilters: - marimo---# UI ElementsOne of marimo's most powerful features is its first-classsupport for interactive user interface (UI) elements: interactingwith a UI element will automatically run cells that reference it.<!---->## `marimo.ui````{.python.marimo}slider = mo.ui.slider(start=1, stop=10, step=1)slidermo.md( f""" The `marimo.ui` module has a library of pre-built elements. For example, here's a `slider`: {slider} """)``````{.python.marimo}mo.md(f"and here's its value: **{slider.value}**.")```### How interactions run cellsWhenever you interact with a UI element, its value is sent back toPython. When this happens, all cells that reference the global variablebound to the UI element, but don't define it, will run.This simple rule lets you use UI elements todrive the execution of your program, letting you buildinteractive notebooks and tools for yourselves and others.```{.python.marimo hide_code="true"}mo.accordion( { "Tip: assign UI elements to global variables": ( """ Interacting with a displayed UI element will only trigger reactive execution if the UI element is assigned to a global variable. """ ), "Tip: accessing an element's value": ( """ Every UI element has a value attribute that you can access in Python. """ ), "Tip: embed UI elements in markdown": mo.md( f""" You can embed UI elements in markdown using f-strings. For example, we can render the slider here: {slider} """ ), })```### Simple elements```{.python.marimo hide_code="true"}mo.md( f""" marimo has a large library of simple UI elements: {basic_ui_elements} """)``````{.python.marimo}selected_element = construct_element(basic_ui_elements.value)show_element(selected_element)``````{.python.marimo}value(selected_element)``````{.python.marimo}documentation(basic_ui_elements.value)``````{.python.marimo hide_code="true"}mo.md( f"""### Composite elements Composite elements are advanced elements let you build UI elements out of other UI elements. Use these powerful elements to logically group together related elements, create a dynamic set of UI elements, or reduce the number of global variables in your program. Select a composite element: {composite_elements} """)``````{.python.marimo}composite_element = construct_element(composite_elements.value)show_element(composite_element)``````{.python.marimo}value(composite_element)``````{.python.marimo}documentation(composite_elements.value)```## State```{.python.marimo hide_code="true"}mo.md( """ **Heads up!** The rest of this tutorial covers state, an advanced topic. Feel free to return here later, if or when you find yourself limited in building interactive stateful apps. """).callout(kind="warn")```You can build powerful interactive notebooks and apps using just`mo.ui` and reactivity.Sometimes, however, you might want interactions to mutate **state**.Maybe you're building a checklist, and you want to maintain a listof action items. Or maybe you want to tie two different UI elements, sothat updating one updates the other.For these and other cases, marimo provides the function `mo.state`, whichcreates state returns a getter function and a setter function. When youcall the setter function in one cell, all othercells that reference the getter via a global variable are automaticallyrun (similar to UI elements).```{.python.marimo}mo.accordion({"Documentation on `mo.state`": mo.doc(mo.state)})```### Creating state`mo.state` takes an initial state value as its argument, and returns- a function that returns the state value;- a function that updates the state value.For exaxmple,```pythonget_counter, set_counter = mo.state(0)``````{.python.marimo}get_counter, set_counter = mo.state(0)``````{.python.marimo hide_code="true"}mo.accordion( { "Tip: assign state getters to global variables": ( """ Calling a state's setter function will only trigger reactive execution if the corresponding getter is assigned to and referenced via a global variable. """ ), "Tip: use state sparingly": ( """ You can get far using just `mo.ui`, without state. That said, judiciously using state can simplify the implementation of highly interactive notebooks/apps, and also enables new use cases. """ ), })``````{.python.marimo}mo.md( f""" Access the value of the state via its getter: `get_counter()` returned **{get_counter()}** """)```### Setting StateSet an element's state by calling its setter function.- Call it with a new value: `set_counter(1)`- Call it with a function that takes the current value and returns a new one: `set_counter(lambda count: count + 1)`**State updates are reactive.** When you call a state's setter in onecell, _all other cells that reference the state getter via a globalvariable_ are automaticallyrun with the new state value. This is similar to how interacting witha UI element automatically runs all cells that use the element.<!---->**The `on_change` callback.** Every UI element takes an optional`on_change` callback, a functionthat takes the new value of the element and does anything with it. You canuse the setter function in an `on_change` callback to mutate state.**🌊 Try it!** Click the button below and watch what happens.```{.python.marimo}increment = mo.ui.button( label="increment", on_change=lambda _: set_counter(lambda v: v + 1),)decrement = mo.ui.button( label="decrement", on_change=lambda _: set_counter(lambda v: v - 1),)mo.hstack([increment, decrement], justify="center")``````{.python.marimo}mo.md( f""" The counter's current value is **{get_counter()}**! This cell runs automatically on button click, even though it doesn't reference either button. """)``````{.python.marimo hide_code="true"}mo.accordion( { "Tip: no self-loops": ( """ Calling a state's setter in one cell won't ever cause that same cell to re-execute, even if it reads that state getter. This prevents accidental infinite loops and makes some things, like tying elements, easier." """ ) })```### Tied elements<!---->Use state to tie two UI elements to the same value.```{.python.marimo}get_shared_state, set_shared_state = mo.state(0)``````{.python.marimo}x = mo.ui.slider( 0, 10, value=get_shared_state(), on_change=set_shared_state, label="$x$:",)``````{.python.marimo}x_plus_one = mo.ui.number( 1, 11, value=get_shared_state() + 1, on_change=lambda v: set_shared_state(v - 1), label="$x + 1$:",)``````{.python.marimo}[x, x_plus_one]``````{.python.marimo hide_code="true"}mo.accordion( { "Tip: tying elements and cycles": ( """ To tie elements, you must `mo.state`, and the tied elements must be created in different cells (since self-loops with state are not allowed). """ ) })```### Example: Task list```{.python.marimo}@dataclassclass Task: name: str done: bool = Falseget_tasks, set_tasks = mo.state([])task_list_mutated, set_task_list_mutated = mo.state(False)``````{.python.marimo}task_list_mutatedtask_entry_box = mo.ui.text(placeholder="a task ...")``````{.python.marimo}def add_task(): if task_entry_box.value: set_tasks(lambda v: v + [Task(task_entry_box.value)]) set_task_list_mutated(True)def clear_tasks(): set_tasks(lambda v: [task for task in v if not task.done]) set_task_list_mutated(True)add_task_button = mo.ui.button( label="add task", on_change=lambda _: add_task(),)clear_tasks_button = mo.ui.button( label="clear completed tasks", on_change=lambda _: clear_tasks())``````{.python.marimo}task_list = mo.ui.array( [ mo.ui.checkbox(value=task.done, label=task.name) for task in get_tasks() ], label="tasks", on_change=lambda v: set_tasks( lambda tasks: [ Task(task.name, done=v[i]) for i, task in enumerate(tasks) ] ),)``````{.python.marimo}mo.hstack( [task_entry_box, add_task_button, clear_tasks_button], justify="start")``````{.python.marimo}mo.as_html(task_list) if task_list.value else mo.md("No tasks! 🎉")```## AppendixThe remaining cells are helper data structures and functions.You can look at their code if you're curious how certain parts of thistutorial were implemented.```{.python.marimo}composite_elements = mo.ui.dropdown( options=dict( sorted( { "array": mo.ui.array, "batch": mo.ui.batch, "dictionary": mo.ui.dictionary, "form": mo.ui.form, }.items() ) ),)``````{.python.marimo}basic_ui_elements = mo.ui.dropdown( options=dict( sorted( { "button": mo.ui.button, "checkbox": mo.ui.checkbox, "date": mo.ui.date, "dropdown": mo.ui.dropdown, "file": mo.ui.file, "multiselect": mo.ui.multiselect, "number": mo.ui.number, "radio": mo.ui.radio, "range_slider": mo.ui.range_slider, "slider": mo.ui.slider, "switch": mo.ui.switch, "tabs": mo.ui.tabs, "table": mo.ui.table, "text": mo.ui.text, "text_area": mo.ui.text_area, }.items() ) ),)``````{.python.marimo}def construct_element(value): if value == mo.ui.array: return mo.ui.array( [mo.ui.text(), mo.ui.slider(1, 10), mo.ui.date()] ) elif value == mo.ui.batch: return mo.md( """ - Name: {name} - Date: {date} """ ).batch(name=mo.ui.text(), date=mo.ui.date()) elif value == mo.ui.button: return mo.ui.button( value=0, label="click me", on_click=lambda value: value + 1 ) elif value == mo.ui.checkbox: return mo.ui.checkbox(label="check me") elif value == mo.ui.date: return mo.ui.date() elif value == mo.ui.dictionary: return mo.ui.dictionary( { "slider": mo.ui.slider(1, 10), "text": mo.ui.text("type something!"), "array": mo.ui.array( [ mo.ui.button(value=0, on_click=lambda v: v + 1) for _ in range(3) ], label="buttons", ), } ) elif value == mo.ui.dropdown: return mo.ui.dropdown(["a", "b", "c"]) elif value == mo.ui.file: return [mo.ui.file(kind="button"), mo.ui.file(kind="area")] elif value == mo.ui.form: return mo.ui.text_area(placeholder="...").form() elif value == mo.ui.multiselect: return mo.ui.multiselect(["a", "b", "c"]) elif value == mo.ui.number: return mo.ui.number(start=1, stop=10, step=0.5) elif value == mo.ui.radio: return mo.ui.radio(["a", "b", "c"], value="a") elif value == mo.ui.range_slider: return mo.ui.range_slider(start=1, stop=10, step=0.5) elif value == mo.ui.slider: return mo.ui.slider(start=1, stop=10, step=0.5) elif value == mo.ui.switch: return mo.ui.switch() elif value == mo.ui.tabs: return mo.ui.tabs( { "Employee #1": { "first_name": "Michael", "last_name": "Scott", }, "Employee #2": { "first_name": "Dwight", "last_name": "Schrute", }, } ) elif value == mo.ui.table: return mo.ui.table( data=[ {"first_name": "Michael", "last_name": "Scott"}, {"first_name": "Dwight", "last_name": "Schrute"}, ], label="Employees", ) elif value == mo.ui.text: return mo.ui.text() elif value == mo.ui.text_area: return mo.ui.text_area() return None``````{.python.marimo}def show_element(element): if element is not None: return mo.hstack([element], "center")``````{.python.marimo}def value(element): if element is not None: v = ( element.value if not isinstance(element, mo.ui.file) else element.name() ) return mo.md( f""" The element's current value is {mo.as_html(element.value)} """ )``````{.python.marimo}def documentation(element): if element is not None: return mo.accordion( { f"Documentation on `mo.ui.{element.__name__}`": mo.doc( element ) } )``````{.python.marimo}from dataclasses import dataclass``````{.python.marimo}import marimo as mo```