| name | marimo-notebook |
| description | Write a marimo notebook in a Python file in the right format. |
Notes for marimo Notebooks
Running Marimo Notebooks
uv run <notebook.py>
uv run marimo run <notebook.py>
uv run marimo edit <notebook.py>
Script Mode Detection
Use mo.app_meta().mode == "script" to detect CLI vs interactive:
@app.cell
def _(mo):
is_script_mode = mo.app_meta().mode == "script"
return (is_script_mode,)
Key Principle: Keep It Simple
Show all UI elements always. Only change the data source in script mode.
- Sliders, buttons, widgets should always be created and displayed
- In script mode, just use synthetic/default data instead of waiting for user input
- Don't wrap everything in
if not is_script_mode conditionals
- Don't use try/except for normal control flow
Good Pattern
@app.cell
def _(ScatterWidget, mo):
scatter_widget = mo.ui.anywidget(ScatterWidget())
scatter_widget
return (scatter_widget,)
@app.cell
def _(is_script_mode, make_moons, scatter_widget, np, torch):
if is_script_mode:
X, y = make_moons(n_samples=200, noise=0.2)
X_data = torch.tensor(X, dtype=torch.float32)
y_data = torch.tensor(y)
data_error = None
else:
X, y = scatter_widget.widget.data_as_X_y
return X_data, y_data, data_error
@app.cell
def _(mo):
lr_slider = mo.ui.slider(start=0.001, stop=0.1, value=0.01)
lr_slider
return (lr_slider,)
@app.cell
def _(is_script_mode, train_button, lr_slider, run_training, X_data, y_data):
if is_script_mode:
results = run_training(X_data, y_data, lr=lr_slider.value)
else:
if train_button.value:
results = run_training(X_data, y_data, lr=lr_slider.value)
return (results,)
Don't Guard Cells with if Statements
Marimo's reactivity means cells only run when their dependencies are ready. Don't add unnecessary guards:
@app.cell
def _(plt, training_results):
if training_results:
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
@app.cell
def _(plt, training_results):
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
The cell won't run until training_results has a value anyway.
Don't Use try/except for Control Flow
Don't wrap code in try/except blocks unless you're handling a specific, expected exception. Let errors surface naturally.
@app.cell
def _(scatter_widget, np, torch):
try:
X, y = scatter_widget.widget.data_as_X_y
X = np.array(X, dtype=np.float32)
except Exception as e:
return None, None, f"Error: {e}"
@app.cell
def _(scatter_widget, np, torch):
X, y = scatter_widget.widget.data_as_X_y
X = np.array(X, dtype=np.float32)
Only use try/except when:
- You're handling a specific, known exception type
- The exception is expected in normal operation (e.g., file not found)
- You have a meaningful recovery action
Cell Output Rendering
Marimo only renders the final expression of a cell. Indented or conditional expressions won't render:
@app.cell
def _(mo, condition):
if condition:
mo.md("This won't show!")
return
@app.cell
def _(mo, condition):
result = mo.md("Shown!") if condition else mo.md("Also shown!")
result
return
Marimo Variable Naming
Variables in for loops that would conflict across cells need underscore prefix:
for _name, _model in items:
...
PEP 723 Dependencies
Prefer pathlib over os.path
Use pathlib.Path for file path operations instead of os.path:
from pathlib import Path
data_dir = Path(tempfile.mkdtemp())
parquet_file = data_dir / "data.parquet"
import os
parquet_file = os.path.join(temp_dir, "data.parquet")
marimo check
When working on a notebook it is important to check if the notebook can run. That's why marimo provides a check command that acts as a linter to find common mistakes.
uvx marimo check <notebook.py>
Make sure these are checked before handing a notebook back to the user.