Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/marimo-team/marimo/llms.txt

Use this file to discover all available pages before exploring further.

marimo makes plots interactive and reactive. Select data in your charts, and marimo automatically updates Python with your selections.

Supported Libraries

marimo provides first-class support for three major plotting libraries:
  • Altair - Declarative visualization library based on Vega-Lite
  • Plotly - Interactive graphing library
  • Matplotlib - Publication-quality static plots with selection overlay

Altair Charts

Basic Usage

import altair as alt
import marimo as mo
from vega_datasets import data

cars = data.cars()

chart = (
    alt.Chart(cars)
    .mark_point()
    .encode(
        x="Horsepower",
        y="Miles_per_Gallon",
        color="Origin",
    )
)

altair_chart = mo.ui.altair_chart(chart)
altair_chart

Accessing Selected Data

# In another cell - automatically reactive
selected_data = altair_chart.value

# selected_data is a dataframe with the selected points
mo.ui.table(selected_data)

Selection Types

# Box/interval selection (default)
chart = mo.ui.altair_chart(
    chart,
    chart_selection="interval"
)
Drag to select a rectangular region.

Custom Selections

If your chart already has selection parameters, marimo respects them:
import altair as alt
import marimo as mo

# Define custom selection
brush = alt.selection_interval()

chart = (
    alt.Chart(data)
    .mark_point()
    .encode(x="x", y="y", color=alt.condition(brush, "Origin:N", alt.value("gray")))
    .add_params(brush)
)

# marimo detects existing selection
altair_chart = mo.ui.altair_chart(chart)

Layered Charts

For layered or concatenated charts, use apply_selection():
import altair as alt
import marimo as mo

base = alt.Chart(cars)

points = base.mark_point().encode(x="Horsepower", y="Miles_per_Gallon")
lines = base.mark_line().encode(x="Horsepower", y="mean(Miles_per_Gallon)")

layered = points + lines
altair_chart = mo.ui.altair_chart(layered)
In another cell:
# Apply selection to original data
selected = altair_chart.apply_selection(cars)
mo.ui.table(selected)

Chart Composition

marimo’s Altair charts support composition operators:
# Horizontal concatenation
combined = chart1 | chart2

# Vertical concatenation  
combined = chart1 & chart2

# Layering
combined = chart1 + chart2

Plotly Charts

Basic Usage

import plotly.express as px
import marimo as mo
from vega_datasets import data

cars = data.cars()

fig = px.scatter(
    cars,
    x="Horsepower",
    y="Miles_per_Gallon",
    color="Origin"
)

plotly_chart = mo.ui.plotly(fig)
plotly_chart

Selection Data

Plotly selections provide multiple data views:
# Selected points as list of dicts
points = plotly_chart.points

# Selection range (for box select)
ranges = plotly_chart.ranges  # {"x": [min, max], "y": [min, max]}

# Selected point indices
indices = plotly_chart.indices

# All selection data
all_data = plotly_chart.value

Supported Chart Types

Scatter & Line

  • scatter
  • scattergl (WebGL)
  • Line charts
  • Area charts

Statistical

  • Bar charts
  • Histograms
  • Box plots
  • Violin plots

Hierarchical

  • Treemap
  • Sunburst
  • Icicle

Specialized

  • Heatmaps
  • Contour plots
  • Geographic maps

Configuration

plotly_chart = mo.ui.plotly(
    fig,
    config={
        "displayModeBar": True,
        "displaylogo": False,
        "toImageButtonOptions": {
            "format": "png",
            "filename": "chart",
            "height": 500,
            "width": 700,
        },
    },
)
See Plotly configuration options for all available settings.

Map Selections

Plotly map traces (scattergeo, scattermapbox) support selection:
import plotly.express as px
import marimo as mo

fig = px.scatter_geo(
    df,
    lat="latitude",
    lon="longitude",
    hover_name="city"
)

map_chart = mo.ui.plotly(fig)
Selections include lat/lon coordinates:
selected = map_chart.points
# [{"lat": 40.7, "lon": -74.0, ...}, ...]

Matplotlib Plots

Interactive Selections

marimo adds interactive selection to static matplotlib plots:
import matplotlib.pyplot as plt
import marimo as mo
import numpy as np

x = np.arange(10)
y = x**2

plt.scatter(x, y)
ax = plt.gca()

mpl_chart = mo.ui.matplotlib(ax)
mpl_chart

Selection Types

1

Box Selection

Click and drag to select a rectangular region.
2

Lasso Selection

Hold Shift and drag to draw a freehand selection.

Using Selection Masks

import numpy as np

x = np.arange(100)
y = np.random.randn(100)

# Get boolean mask for selected points
mask = mpl_chart.value.get_mask(x, y)

# Filter data
selected_x = x[mask]
selected_y = y[mask]

mo.md(f"Selected {len(selected_x)} points")

Selection Objects

The value attribute returns different selection types:
from marimo.ui.matplotlib import BoxSelection, LassoSelection, EmptySelection

selection = mpl_chart.value

if isinstance(selection, BoxSelection):
    mo.md(f"Box: ({selection.x_min}, {selection.y_min}) to ({selection.x_max}, {selection.y_max})")
elif isinstance(selection, LassoSelection):
    mo.md(f"Lasso with {len(selection.vertices)} vertices")
else:  # EmptySelection
    mo.md("No selection")

Debouncing

Control when selections are sent to Python:
# Only update on mouse-up (default: False)
mpl_chart = mo.ui.matplotlib(ax, debounce=True)

Reactive Visualizations

Combine charts with other UI elements for dashboards:
import altair as alt
import marimo as mo

# Filter slider
min_hp = mo.ui.slider(50, 250, value=50, label="Min Horsepower")

# Reactive chart
chart = (
    alt.Chart(cars)
    .mark_point()
    .encode(x="Horsepower", y="Miles_per_Gallon", color="Origin")
    .transform_filter(alt.datum.Horsepower >= min_hp.value)
)

mo.ui.altair_chart(chart)
Whenever min_hp changes, the chart automatically updates.

Performance Tips

For large datasets, consider these optimizations:
  • Altair: Use mark_point() with size encoding instead of large markers
  • Plotly: Use scattergl instead of scatter for 10k+ points
  • Matplotlib: Selections are computed in JavaScript, so they remain fast
  • Data transformers: Altair supports vegafusion for server-side processing

VegaFusion

For very large Altair datasets:
import altair as alt

# Enable vegafusion transformer
alt.data_transformers.enable('vegafusion')

# Charts now process data server-side
chart = alt.Chart(large_df).mark_point().encode(x="x", y="y")
mo.ui.altair_chart(chart)
marimo automatically detects vegafusion and adjusts chart rendering accordingly.

Theming

Charts respect marimo’s dark/light mode:
  • Altair: Backgrounds are automatically transparent
  • Plotly: Uses the configured renderer theme
  • Matplotlib: Renders with current style settings

Best Practices

  1. Keep charts reactive - Store chart objects in variables and reference UI values
  2. Use appropriate selection types - Point selection for discrete data, interval for continuous
  3. Handle empty selections - Check if data is selected before processing
  4. Combine with dataframes - Use mo.ui.dataframe() to show selected data in tabular form
  5. Optimize for size - Sample or aggregate data before plotting millions of points

Example: Interactive Dashboard

import altair as alt
import marimo as mo
from vega_datasets import data

cars = data.cars()

# Interactive scatter plot
scatter = mo.ui.altair_chart(
    alt.Chart(cars)
    .mark_point()
    .encode(x="Horsepower", y="Miles_per_Gallon", color="Origin")
)

# Display chart and selected data side-by-side
mo.hstack([scatter, mo.ui.table(scatter.value)])
This creates a fully reactive dashboard where selecting points updates the table automatically.

Build docs developers (and LLMs) love