Plotly
Plotly builds interactive, web-ready charts with hover, zoom, and pan - export HTML or embed in Dash apps.
Search across all documentation pages
Plotly builds interactive, web-ready charts with hover, zoom, and pan - export HTML or embed in Dash apps.
Quick-reference recipe card - copy-paste ready.
import plotly.express as px
import pandas as pd
df = pd.read_csv("sales.csv", parse_dates=["ordered_at"])
fig = px.line(df, x="ordered_at", y="revenue", color="region", title="Revenue trend")
fig.update_layout(hovermode="x unified")
fig.write_html("revenue.html", include_plotlyjs="cdn")When to reach for this:
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
df = pd.DataFrame(
{
"region": np.repeat(["East", "West"], 100),
"month": np.tile(np.arange(1, 13).repeat(8)[:100], 2),
"revenue": rng.integers(80, 200, 200),
"units": rng.integers(5, 50, 200),
}
)
# Express scatter with custom hover
fig_scatter = px.scatter(
df,
x="units",
y="revenue",
color="region",
hover_data=["month"],
opacity=0.7,
title="Units vs revenue",
)
fig_scatter.update_traces(marker=dict(size=8))
# Graph objects for dual-axis combo
monthly = df.groupby(["month", "region"], observed=True)["revenue"].sum().reset_index()
fig_combo = go.Figure()
for region, sub in monthly.groupby("region"):
fig_combo.add_trace(
go.Scatter(x=sub["month"], y=sub["revenue"], mode="lines+markers", name=region)
)
fig_combo.update_layout(
xaxis_title="Month",
yaxis_title="Revenue",
legend_title="Region",
template="plotly_white",
)
fig_scatter.write_html("scatter.html", include_plotlyjs="cdn")
fig_combo.write_html("combo.html", include_plotlyjs="cdn")What this demonstrates:
hover_datatemplate for consistent light theme| Layer | Best for |
|---|---|
plotly.express | Standard charts from tidy data |
plotly.graph_objects | Custom layouts, dual axes, annotations |
import plotly.io as pio
# Static image export needs kaleido
pio.write_image(fig, "chart.png", scale=2)include_plotlyjs=True bloats files. Fix: use "cdn" or "directory".astype(str) or category_orders.hovertemplate.write_image fails in CI. Fix: uv pip install kaleido in export jobs.
| Alternative | Use When | Don't Use When |
|---|---|---|
| matplotlib | Print/PDF static pipelines | Need hover tooltips |
| Altair | Vega-Lite declarative specs | Need 3-D WebGL traces |
| Bokeh | Large streaming datasets | Team already on Plotly/Dash |
| Observable Plot | Web-native grammar | Python-only batch jobs |
fig.update_traces(hovertemplate="Revenue: %{y:$,.0f}<extra></extra>")fig.show() renders inline with nbformat support.fig.update_layout(legend_traceorder="reversed")fig.add_hline(y=150, line_dash="dash", annotation_text="target")pl.DataFrame where supported or .to_pandas() first.fig.update_layout(template="plotly_dark")fig.to_html(full_html=False) in templates.px.scatter(..., animation_frame="year") for small datasets.Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026