docs/faq.md
<a name="faq-jupyter"></a>
marimo is a reinvention of the Python notebook as a reproducible, interactive, and shareable Python program that can be executed as scripts or deployed as interactive web apps.
Consistent state. In marimo, your notebook code, outputs, and program state are guaranteed to be consistent. Run a cell and marimo reacts by automatically running the cells that reference its variables. Delete a cell and marimo scrubs its variables from program memory, eliminating hidden state.
Built-in interactivity. marimo also comes with UI elements like sliders, a dataframe transformer, and interactive plots that are automatically synchronized with Python. Interact with an element and the cells that use it are automatically re-run with its latest value.
Pure Python programs. Unlike Jupyter notebooks, marimo notebooks are stored as pure Python files that can be executed as scripts, deployed as interactive web apps, and versioned easily with Git.
<a name="faq-problems"></a>
marimo solves problems in reproducibility, maintainability, interactivity, reusability, and shareability of notebooks.
Reproducibility. In Jupyter notebooks, the code you see doesn't necessarily match the outputs on the page or the program state. If you delete a cell, its variables stay in memory, which other cells may still reference; users can execute cells in arbitrary order. This leads to widespread reproducibility issues. One study analyzed 10 million Jupyter notebooks and found that 36% of them weren't reproducible.
In contrast, marimo guarantees that your code, outputs, and program state are consistent, eliminating hidden state and making your notebook reproducible. marimo achieves this by intelligently analyzing your code and understanding the relationships between cells, and automatically re-running cells as needed.
In addition, marimo notebooks can serialize package requirements inline; marimo runs these "sandboxed" notebooks in temporary virtual environments, making them reproducible down to the packages.
Maintainability.
marimo notebooks are stored as pure Python programs (.py files). This lets you
version them with Git; in contrast, Jupyter notebooks are stored as JSON and
require extra steps to version.
Interactivity. marimo notebooks come with UI elements that are automatically synchronized with Python (like sliders, dropdowns); eg, scrub a slider and all cells that reference it are automatically re-run with the new value. This is difficult to get working in Jupyter notebooks.
Reusability.
marimo notebooks can be executed as Python scripts from the command-line (since
they're stored as .py files). In contrast, this requires extra steps to
do for Jupyter, such as copying and pasting the code out or using external
frameworks. We also let you import symbols (functions, classes) defined in a
marimo notebook into other Python programs/notebooks, something you can't
easily do with Jupyter.
Shareability.
Every marimo notebook can double as an interactive web app, complete with UI
elements, which you can serve using the marimo run command. This isn't
possible in Jupyter without substantial extra effort.
To learn more about problems with traditional notebooks, see these references [1] [2].
<a name="faq-widgets"></a>
marimo.ui different from Jupyter widgets?Unlike Jupyter widgets, marimo's interactive elements are automatically synchronized with the Python kernel: no callbacks, no observers, no manually re-running cells.
<p align="center"> <video autoplay muted loop playsinline width="600px" align="center"> <source src="/_static/faq-marimo-ui.mp4" type="video/mp4"> <source src="/_static/faq-marimo-ui.webm" type="video/webm"> </video> </p><a name="faq-notebook-or-library"></a>
marimo is both a notebook and a library.
marimo edit.import marimo as mo) in
marimo notebooks. Write markdown with mo.md(...),
create stateful interactive elements with mo.ui (mo.ui.slider(...)), and
more. See the docs for an API reference.<a name="faq-notebook-app"></a>
marimo programs are notebooks, apps, or both, depending on how you use them.
There are two ways to interact with a marimo program:
marimo editmarimo runAll marimo programs start as notebooks, since they are created with marimo edit. Because marimo notebooks are reactive and have built-in interactive
elements, many can easily be made into useful and beautiful apps by simply
hiding the notebook code: this is what marimo run does.
Not every notebook needs to be run as an app — marimo notebooks are useful in
and of themselves for rapidly exploring data and doing reproducible science.
And not every app is improved by interacting with the notebook. In some
settings, such as collaborative research, education, and technical
presentations, going back and forth between the notebook view and app view
(which you can do from marimo edit) can be useful!
<a name="faq-reactivity"></a>
marimo reads each cell once to determine what global names it defines and what global names it reads. When a cell is run, marimo runs all other cells that read any of the global names it defines. A global name can refer to a variable, class, function, or import.
In other words, marimo uses static analysis to make a dataflow graph out of your cells. Each cell is a node in the graph across which global variables "flow". Whenever a cell is run, either because you changed its code or interacted with a UI element it reads, all its descendants run in turn.
<a name="faq-overhead"></a>
No, marimo doesn't slow your code down. marimo determines the dependencies among cells by reading your code, not running or tracing it, so there's zero runtime overhead.
<a name="faq-expensive"></a>
Reactive (automatic) execution ensures your code and outputs are always in sync, improving reproducibility by eliminating hidden state and out-of-order execution; marimo also takes care to run only the minimal set of cells needed to keep your notebook up to date. But when some cells take a long time to run, it's understandable to be concerned that automatic execution will kick off expensive cells before you're ready to run them.
Here are some tips to avoid accidental execution of expensive cells:
mo.stop][marimo.stop] to conditionally stop
execution of a cell and its descendants.mo.cache][marimo.cache] to cache
expensive intermediate computations.mo.persistent_cache][marimo.persistent_cache] to cache variables to
disk; on re-run, marimo will read values from disk instead of recalculating
them as long as the cell is not stale.<a name="faq-lazy"></a>
You can disable automatic execution through the notebook runtime settings; see the guide on runtime configuration.
When automatic execution is disabled, marimo still gives you guarantees on your notebook state and automatically marks cells as stale when appropriate.
<a name="faq-interactivity"></a>
Interactive UI elements like sliders are available in marimo.ui.
slider = mo.ui.slider(0, 100))slider or mo.md(f"Choose a value: {slider}"))value attribute (slider.value)When a UI element bound to a global variable is interacted with, all cells referencing the global variable are run automatically.
If you have many UI elements or don't know the elements
you'll create until runtime, use marimo.ui.array and marimo.ui.dictionary
to create UI elements that wrap other UI elements (sliders = mo.ui.array([slider(1, 100) for _ in range(n_sliders)])).
All this and more is explained in the UI tutorial. Run it with
marimo tutorial ui
at the command line.
<a name="faq-form"></a>
Use the form method to add a submit button to a UI element. For
example,
form = marimo.ui.text_area().form()
When wrapped in a form, the
text area's value will only be sent to Python when you click the submit button.
Access the last submitted value of the text area with form.value.
<a name="faq-markdown"></a>
Import marimo (as mo) in a notebook, and use the mo.md function.
Learn more in the outputs guide
or by running marimo tutorial markdown.
<a name="faq-plots"></a>
Include plots in the last expression of a cell to display them, just like all
other outputs. If you're using matplotlib, you can display the Figure object
(get the current figure with plt.gcf()). For examples, run the plots tutorial:
marimo tutorial plots
Also see the plotting API reference.
<a name="faq-mpl-cutoff"></a>
If your legend or axes labels are cut off, try calling plt.tight_layout()
before outputting your plot:
import matplotlib.pyplot as plt
plt.plot([-8, 8])
plt.ylabel("my variable")
plt.tight_layout()
plt.gca()
<a name="faq-interactive-plots"></a>
Use [marimo.mpl.interactive][marimo.mpl.interactive].
fig, ax = plt.subplots()
ax.plot([1, 2])
mo.mpl.interactive(ax)
<a name="faq-rows-columns"></a>
Use marimo.hstack and marimo.vstack. See the layout tutorial for details:
marimo tutorial layout
<a name="faq-show-code"></a>
Use [mo.show_code][marimo.show_code].
<a name="faq-dynamic-ui-elements"></a>
Use [mo.ui.array][marimo.ui.array],
[mo.ui.dictionary][marimo.ui.dictionary], or
[mo.ui.batch][marimo.ui.batch] to create a UI element
that wraps a dynamic number of other UI elements.
If you need custom
formatting, use [mo.ui.batch][marimo.ui.batch], otherwise
use [mo.ui.array][marimo.ui.array] or
[mo.ui.dictionary][marimo.ui.dictionary].
For usage examples, see the recipes for grouping UI elements together.
To create an interruptible progress bar, run the progress bar in its own thread, and create a button that on change signals to the thread that it should exit.
Example:
import marimo
__generated_with = "0.20.1"
app = marimo.App()
@app.cell
def _():
import marimo as mo
import time
from threading import Event
return Event, mo, time
@app.cell
def _(Event):
cancelled = Event()
return (cancelled,)
@app.cell
def _(cancelled, mo):
cancel = mo.ui.button(
label="Interrupt the progress bar", on_change=lambda _: cancelled.set()
)
cancel
return
@app.cell
def _(cancelled, mo, time):
def progress(total):
with mo.status.progress_bar(total=10) as pbar:
for _ in range(10):
if cancelled.is_set():
pbar.update(
increment=0, subtitle="The user cancelled the iteration"
)
break
# Sleep... or anything else that releases GIL
time.sleep(0.5)
pbar.update()
return (progress,)
@app.cell
def _(mo, progress):
mo.Thread(target=progress, args=(10,)).start()
return
if __name__ == "__main__":
app.run()
<a name="faq-restart"></a>
To clear all program memory and restart the notebook from scratch, open the notebook menu in the top right and click "Restart kernel".
<a name="faq-reload"></a>
Enable automatic reloading of modules via the runtime settings in your marimo installation's user configuration. (Click the "gear" icon in the top right of a marimo notebook).
When enabled, marimo will automatically hot-reload modified modules before executing a cell.
<a name="faq-on-change-called"></a>
on_change/on_click handlers being called?A UI Element's on_change (or for buttons, on_click) handlers are only
called if the element is bound to a global variable. For example, this won't work
mo.vstack([mo.ui.button(on_change=lambda _: print("I was called")) for _ in range(10)])
In such cases (when you want to output a dynamic number of UI elements),
you need to use
[mo.ui.array][marimo.ui.array],
[mo.ui.dictionary][marimo.ui.dictionary], or
[mo.ui.batch][marimo.ui.batch].
See the recipes for grouping UI elements together for example code.
<a name="faq-on-change-last"></a>
on_change handlers in an array all referencing the last element?Don't do this: In the below snippet, every on_change will print 9!.
array = mo.ui.array(
[mo.ui.button(on_change=lambda value: print(i)) for i in range(10)
])
Instead, do this: Explicitly bind i to the current loop value:
array = mo.ui.array(
[mo.ui.button(on_change=lambda value, i=i: print(i)) for i in range(10)]
)
array
This is necessary because in Python, closures are late-binding.
<a name="faq-sql-brackets"></a>
Our "SQL" cells are really just Python under the hood to keep notebooks as pure Python scripts. By default, we use f-strings for SQL strings, which allows for parameterized SQL like SELECT * from table where value < {min}.
To escape real { / } that you don't want parameterized, use double \{\{...\}\}:
SELECT unnest([\{\{'a': 42, 'b': 84\}\}, \{\{'a': 100, 'b': NULL\}\}]);
<a name="faq-annotations"></a>
Type annotations are registered as references of a cell, unless they are explicitly written as strings. This helps ensure correctness of code that depends on type annotations at runtime (e.g., Pydantic), while still providing a way to omit annotations from affecting dataflow graph.
For example, in
x: A = ...
A is treated as a reference, used in determining the dataflow graph, but
in
x: "A" = ...
A isn't made a reference.
For Python 3.12+, marimo additionally implements annotation scoping.
<a name="faq-dotenv"></a>
The package dotenv's loadenv() function does not work out-of-the box in
marimo. Instead, use dotenv.load_dotenv(dotenv.find_dotenv(usecwd=True)).
<a name="faq-packages"></a>
You can use any Python package. marimo cells run arbitrary Python code.
<a name="faq-remote"></a>
We recorded a video tutorial on how to use marimo on a remote server. Check it out here.
Use SSH port-forwarding to run marimo on a remote server
and connect to it from a browser on your local machine. Make sure
to pass the --headless flag when starting marimo on remote; on the remote
machine, we also recommend using a port other than marimo's default port, such
as 8080:
On the remote machine, run:
marimo edit --headless --port 8080
or, if you want to set a custom host:
marimo edit --headless --host 0.0.0.0 --port 8080
On local, run:
ssh -N -L 3718:127.0.0.1:8080 REMOTE_USER@REMOTE_HOST
Then open localhost:3718 in your browser.
<a name="faq-interfaces"></a>
Use --host 0.0.0.0 with marimo edit, marimo run, or marimo tutorial:
marimo edit --host 0.0.0.0
<a name="faq-jupyter-hub"></a>
JupyterHub can be configured to launch marimo using the
marimo-jupyter-extension.
<a name="faq-jupyter-book"></a>
JupyterBook makes it easy to create static websites with markdown and Jupyter notebooks.
To include a marimo notebook in a JupyterBook, you can either export your
notebook to an ipynb file, or export to HTML:
marimo export ipynb my_notebook.py -o my_notebook.ipynb --include-outputsmarimo export html my_notebook.py -o my_notebook.html<a name="faq-app-deploy"></a>
Use the marimo CLI's run command to serve a notebook as an app:
marimo run notebook.py
If you are running marimo inside a Docker container, you may want to run under a different host and port:
marimo run notebook.py --host 0.0.0.0 --port 8080
<a name="faq-marimo-free"></a>
Yes!