Data Storytelling with Marimo Notebooks

Traditional data science notebooks suffer from hidden state bugs, out-of-order cell executions, and poor reproducibility when shared with business stakeholders.

By utilizing Marimo—an open-source reactive Python notebook—you can build reproducible, interactive data storytelling apps that run entirely in the browser and automatically re-execute downstream dependencies when inputs change.

Core Mechanics of Reactive Notebooks

Marimo redefines notebook execution through a deterministic dataflow architecture instead of traditional Jupyter mutable state states:

  • Deterministic Dataflow: Cells are reactive graph nodes; when a variable changes, all dependent downstream cells automatically update without manual re-runs.
  • Pure Python Files (.py): Notebooks are stored as clean, version-control-friendly Python scripts rather than complex JSON structures containing execution outputs.
  • Built-in UI Components: Interactive sliders, dropdowns, and tables natively bind to Python variables without requiring external dashboarding libraries like Streamlit or Dash.

Jupyter Notebooks vs. Marimo Notebooks

Evaluation MetricJupyter NotebooksMarimo Notebooks
Execution ModelImperative; relies on manual hidden cell execution orderReactive; automatically tracks variables and executes dependency graphs
Version ControlJSON-based format prone to messy git diff conflicts and output bloatPure Python script format (.py); clean diffs and executable as standard CLI apps
Best Suited ForExploratory scratchpads and ad-hoc data experimentationReproducible research reports, interactive data apps, and shareable tutorials

Marimo Interactive Data App Blueprint

Here is how to define interactive UI elements and reactive data transformations using Marimo in Python:

Python
Building interactive reactive data apps using Marimo UI elements
import marimo as mo
import pandas as pd
import numpy as np

# 1. Create interactive UI slider for data filtering
threshold_slider = mo.ui.slider(
    start=10,
    stop=100,
    step=5,
    value=50,
    label="Select Revenue Threshold ($k):"
)

# Display the interactive slider widget in the notebook cell
threshold_slider

# 2. Reactive Data Transformation Cell
# This cell automatically re-executes whenever threshold_slider.value changes
np.random.seed(42)
n_records = 200

df = pd.DataFrame({
    "customer_id": range(1000, 1000 + n_records),
    "monthly_revenue": np.random.randint(5, 120, size=n_records)
})

# Filter DataFrame based on reactive slider input
filtered_df = df[df["monthly_revenue"] >= threshold_slider.value]

# 3. Display summary metrics reactively
mo.md(f"### Filtered Results\n- **Active Threshold:** ${threshold_slider.value}k\n- **Matching Customers:** {len(filtered_df)} / {len(df)}")

Data Storytelling Best Practices

When designing interactive data science narratives and stakeholder dashboards using Marimo, follow these guidelines:

  • Modularize Notebook Cells: Keep individual cells small and focused on single transformation or visualization steps to maintain a clear reactive dependency graph.
  • Leverage Markdown Interactivity: Combine mo.md() string formatting with UI widget values to write dynamic, self-narrating reports that update instantly as readers explore parameters.
  • Publish as Standalone Apps: Run marimo run app.py in production to deploy your reactive data notebooks as clean web applications for executive stakeholders.