On this page
This Plotly tutorial shows you how to make interactive charts in Python: install plotly, pass a DataFrame to one Plotly Express call such as px.line, and you get a chart you can hover over, zoom into and pan around. Where matplotlib produces static images, Plotly gives you that interactivity with no extra work.
The defaults hide a few surprises. A blank figure in Jupyter often comes down to outdated Notebook packages, and the default HTML export of one chart below weighed 4.8 MB against under 40 KB with a single argument changed. This first part is for Python users who know a little pandas and want charts for screens: Express versus Graph Objects, the everyday chart types, styling, faceting and saving.
As of September 2026 the current release is Plotly 7.1. The examples here were run on Plotly 6.3, and the Plotly 7.0 release notes removes nothing they use.
How do you install Plotly?
Run pip install plotly (with pandas for the examples), and add anywidget if you work in Jupyter:
pip install plotly pandas
If you use conda, conda install -c conda-forge plotly works too. For notebooks, the Plotly getting-started page lists jupyterlab and anywidget for JupyterLab, or notebook>=7.0 and anywidget for the classic interface. If you still need a Python environment, the Anaconda on Windows guide covers the setup.
What are Plotly Express and Graph Objects?
Plotly has a high-level and a low-level interface, and knowing which you are using prevents most confusion.
Plotly Express (plotly.express, conventionally px) builds a whole figure from a DataFrame in one call. It handles the large majority of charts.
Graph Objects (plotly.graph_objects, conventionally go) constructs figures trace by trace. It is more verbose, but necessary when you need control Express does not expose.
Express returns a Graph Objects figure, so you can start high-level and refine afterwards. That is the workflow to aim for.
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
How do you make basic charts with Plotly Express?
Plotly Express works best with tidy data: one row per observation, one column per variable.
df = pd.DataFrame({
'month': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
'revenue': [120, 145, 132, 178, 195],
'region': ['North', 'North', 'North', 'North', 'North'],
})
fig = px.line(df, x='month', y='revenue', title='Monthly revenue')
fig.show()
That is a complete interactive chart. Axis labels come from the column names, and hover and zoom work straight away.
Bar chart:
fig = px.bar(df, x='month', y='revenue',
title='Revenue by month',
labels={'revenue': 'Revenue (₹k)', 'month': 'Month'})
fig.show()
Scatter, with a third variable encoded as colour and a fourth as size:
world = px.data.gapminder().query("year == 2007") # 142 countries
fig = px.scatter(
world,
x='gdpPercap', y='lifeExp',
size='pop', color='continent',
hover_name='country',
log_x=True,
size_max=55,
title='Life expectancy vs GDP per capita, 2007',
)
fig.show()
hover_name is the small detail that makes Plotly worth using, because hovering a point names the country rather than showing bare coordinates. The gapminder sample data ships with Plotly, so this runs offline.
Histogram and box plot:
px.histogram(world, x='lifeExp', nbins=30)
px.box(world, x='continent', y='lifeExp', points='outliers')
Worked example: tidy data with several series
When you have more than one region, keep the data long and let color split the lines:
sales = pd.DataFrame({
'month': ['Jan', 'Feb', 'Mar'] * 2,
'region': ['North'] * 3 + ['South'] * 3,
'revenue': [120, 145, 132, 90, 110, 128],
})
fig = px.line(sales, x='month', y='revenue', color='region', markers=True)
fig.show()
Each region becomes its own trace with its own legend entry. Plotly Express also accepts wide data, for example y=['north', 'south'] for two columns, but long data scales better once you add faceting.
How do you style a Plotly chart?
Use update_layout for titles, axes, templates and hover behaviour, and update_traces for lines and markers. Most adjustments go through update_layout:
fig = px.line(df, x='month', y='revenue')
fig.update_layout(
title={'text': 'Monthly revenue', 'x': 0.02, 'font': {'size': 20}},
xaxis_title='Month',
yaxis_title='Revenue (₹ thousands)',
template='plotly_white',
hovermode='x unified',
margin={'l': 60, 'r': 30, 't': 60, 'b': 50},
showlegend=False,
)
fig.show()
Two of those are worth calling out. template='plotly_white' replaces the default grey background with something publishable. Meanwhile, hovermode='x unified' shows every series at the hovered x-position in one tooltip, which is almost always what you want on a multi-line chart.
Trace-level styling uses update_traces:
fig.update_traces(line={'width': 3, 'color': '#2563eb'},
mode='lines+markers')
When should you drop to Graph Objects?
When Express cannot express what you need, typically mixing chart types, build the figure directly:
fig = go.Figure()
fig.add_trace(go.Bar(
x=df['month'], y=df['revenue'],
name='Revenue', marker_color='#2563eb',
))
fig.add_trace(go.Scatter(
x=df['month'], y=df['revenue'].rolling(2).mean(),
name='2-month average',
mode='lines+markers',
line={'color': '#f59e0b', 'width': 3},
))
fig.update_layout(title='Revenue with trend',
template='plotly_white')
fig.show()
The pattern is always the same: create a go.Figure(), call add_trace for each series, then update_layout. The two-month average starts with a gap because the first month has no previous value to average with.
How do you make small multiples in Plotly?
Pass facet_col (and facet_col_wrap) to a Plotly Express function. Small multiples in one argument are Express's strongest feature:
gap = px.data.gapminder()
fig = px.line(
gap.query("continent == 'Asia'"),
x='year', y='lifeExp',
facet_col='country', facet_col_wrap=5,
height=800,
)
fig.update_yaxes(matches=None) # independent y-axes per panel
fig.show()
The Asia subset has 33 countries, so this draws seven rows of panels. Doing that by hand in Graph Objects takes dozens of lines. The facet plots guide covers facet_row and shared axes in more depth.
How do you save a Plotly chart?
Use write_html for an interactive file and write_image for PNG or PDF:
fig.write_html('chart.html') # interactive, self-contained
fig.write_html('chart_cdn.html', include_plotlyjs='cdn') # small, loads Plotly.js online
The default HTML file embeds the whole Plotly.js library, so it is large (4.8 MB for the facet chart above on Plotly 6.3) but works offline. With include_plotlyjs='cdn' the same chart shrank to under 40 KB, although the reader then needs an internet connection. The HTML export guide explains the other options.
For a PNG or PDF, use fig.write_image('chart.png', scale=2). According to the static image export guide, this needs Kaleido 1.0 or later and a Chrome or Chromium install on the machine.
Plotly Express quick reference
This table collects the functions used in this Plotly tutorial:
| Chart | Function | Key arguments |
|---|---|---|
| Line | px.line |
x, y, color, markers=True |
| Bar | px.bar |
x, y, color, barmode |
| Scatter / bubble | px.scatter |
size, color, hover_name, log_x |
| Distribution | px.histogram |
x, nbins |
| Groups | px.box |
x, y, points |
| Small multiples | any of the above | facet_col, facet_col_wrap |
Common mistakes in a first Plotly project
- Passing wide data with many series. Reshape to long form with
pandas.melt, then usecolor. - Calling
write_imagewithout Kaleido or Chrome. Install Kaleido 1.0+, then runplotly_get_chromeif no browser is found. - Emailing 5 MB HTML files. Use
include_plotlyjs='cdn'when the reader will be online. - Styling every chart by hand. Set
template='plotly_white'once, then change only what matters. - Using an old Jupyter Notebook. Plotly 6 and later need Notebook 7 or JupyterLab.
What comes next in this Plotly tutorial?
Part two covers subplots, secondary axes, animation, 3D charts and annotation: the customisation that turns a working chart into a presentable one.
The Sunday Growth Brief
One email a week: the best new comparisons, a fresh roadmap and the tech news worth your attention.
No spam. Unsubscribe in one click.
Related reading
Plotly Part 2: advanced charts continues this series. For the static alternative, see matplotlib styling and customisation. Plotly charts are built on arrays, so the NumPy tutorial series is useful background.
Frequently asked questions
Should I use Plotly Express or Graph Objects?
Start with Plotly Express. It builds a complete figure from a DataFrame in one call and returns a normal Graph Objects figure, so you can refine it afterwards. Move to Graph Objects when you need several trace types on one set of axes, fine-grained control over each trace, or a chart type that Express does not cover.
Should I use Plotly or matplotlib?
Use Plotly for anything read on a screen where hover and zoom help, such as dashboards, exploratory analysis and charts shared with colleagues. Use matplotlib for print, academic papers and precise static layouts. Many analysts use both: Plotly while exploring the data, and matplotlib when a figure has to go into a PDF or a slide at a fixed size.
Do Plotly charts work in Jupyter?
Yes, they render inline and stay interactive. Plotly's own instructions for JupyterLab are to install jupyterlab and anywidget in the same environment. For the classic interface you need Jupyter Notebook 7 or later plus anywidget, because Plotly 6 dropped support for Notebook 6. If a figure appears blank, check those package versions first.
How do I save a Plotly chart as an image?
fig.write_html('chart.html') keeps full interactivity and needs nothing extra. fig.write_image('chart.png') produces a static image but requires Kaleido 1.0 or later, which in turn needs Chrome or Chromium on the machine. If Chrome is missing, Plotly provides the plotly_get_chrome command to install a copy it can use.
Written by
Vanshika Nigam
ETL & Data Engineer, Accenture
ETL developer at Accenture working with Informatica, Snowflake and DBT, specialising in data mapping, cleansing and pipeline performance.

