How to use Plotly in Python
Learn to use Plotly in Python. Explore different methods, tips, real-world applications, and how to debug common errors in this guide.

Plotly is a Python library that helps you create interactive, publication-quality graphs. It offers a high-level interface for complex charts, which makes data visualization both powerful and straightforward for developers.
In this article, we'll explore core techniques and practical tips to get you started. You'll find real-world applications and debugging advice to help you master Plotly for your own data storytelling needs.
Creating your first Plotly line chart
import plotly.express as px
df = px.data.gapminder().query("country=='Canada'")
fig = px.line(df, x="year", y="lifeExp", title="Life expectancy in Canada")
fig.show()--OUTPUT--[Interactive line chart showing life expectancy in Canada over time from 1952 to 2007]
This example uses plotly.express, aliased as px, which is Plotly's high-level interface for rapidly creating figures. The code first prepares the data by loading a built-in sample dataset with px.data.gapminder() and filtering it for Canada.
The key function is px.line(). You simply pass it the DataFrame and specify which columns map to the x and y axes. It’s a declarative approach—you describe the chart you want, and Plotly handles the rendering details. Finally, fig.show() displays the interactive visualization.
Essential Plotly visualization techniques
Just as you created a line chart, you can use Plotly's expressive syntax to build other fundamental visualizations for comparing quantities or illustrating data composition.
Building a bar chart with px.bar()
import plotly.express as px
df = px.data.gapminder().query("year==2007").sort_values("gdpPercap")
fig = px.bar(df.tail(10), x="country", y="gdpPercap", color="continent")
fig.show()--OUTPUT--[Interactive bar chart showing GDP per capita for the top 10 wealthiest countries in 2007, colored by continent]
Creating a bar chart is just as direct. The code first prepares the data by filtering for the year 2007, then uses tail(10) to select the 10 countries with the highest GDP per capita.
- The
px.bar()function maps countries to the x-axis and their GDP per capita to the y-axis. - The
colorparameter is a powerful feature. By settingcolor="continent", you instruct Plotly to automatically color-code the bars based on the continent and generate a corresponding legend.
Creating interactive scatter plots
import plotly.express as px
df = px.data.gapminder().query("year==2007")
fig = px.scatter(df, x="gdpPercap", y="lifeExp", size="pop", color="continent",
hover_name="country", log_x=True, size_max=60)
fig.show()--OUTPUT--[Interactive scatter plot showing relationship between GDP per capita and life expectancy, with points sized by population and colored by continent]
The px.scatter() function is ideal for visualizing relationships between variables. It lets you map multiple data dimensions onto visual properties, creating a richer chart.
- The
size="pop"argument scales each marker based on population, adding another dimension to the plot. hover_name="country"enhances interactivity by displaying the country name in the tooltip on hover.- Using
log_x=Trueapplies a logarithmic scale, which is useful for spreading out data points when values span a wide range.
Designing a pie chart for data composition
import plotly.express as px
df = px.data.gapminder().query("year==2007 and continent=='Europe'")
fig = px.pie(df, values="pop", names="country", title="Population of European countries")
fig.update_traces(textposition='inside', textinfo='percent+label')
fig.show()--OUTPUT--[Interactive pie chart showing population distribution across European countries with percentage labels]
Pie charts are perfect for illustrating how individual parts make up a whole. The px.pie() function builds the chart by mapping data to slices.
- The
valuesparameter, set to"pop", determines the size of each slice based on population. - The
namesparameter, set to"country", labels each slice with its corresponding country.
You can then fine-tune the appearance. The fig.update_traces() method customizes the labels, placing them inside each slice and formatting them to show both the percentage and the country name.
Advanced Plotly features
With the fundamentals in place, you can now move beyond single charts to build more sophisticated layouts, create dynamic animations, and apply custom styling.
Creating multi-chart layouts with subplots
import plotly.graph_objects as go
from plotly.subplots import make_subplots
fig = make_subplots(rows=1, cols=2)
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]), row=1, col=1)
fig.add_trace(go.Bar(x=[1, 2, 3], y=[7, 8, 9]), row=1, col=2)
fig.update_layout(title_text="Side-by-side charts using subplots")
fig.show()--OUTPUT--[Interactive dashboard with two charts side by side - a scatter plot on the left and a bar chart on the right]
For more complex layouts, you'll work with plotly.graph_objects, often aliased as go. This gives you more granular control than plotly.express. The make_subplots() function is your starting point for creating a grid to hold multiple charts.
- First, you initialize a figure with a grid layout using
make_subplots(), specifying the number of rows and columns. - Then, you add each chart—or trace—to the figure using
fig.add_trace(), assigning it to a specificrowandcol.
Building animated visualizations
import plotly.express as px
df = px.data.gapminder()
fig = px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year",
size="pop", color="continent", hover_name="country", log_x=True)
fig.show()--OUTPUT--[Interactive animated scatter plot showing how countries' GDP and life expectancy changed from 1952 to 2007, with play/pause controls]
Plotly makes creating animations surprisingly simple, often requiring just one additional parameter. The key is animation_frame, which turns a static chart into a dynamic story.
- By setting
animation_frame="year", you instruct Plotly to generate a separate frame for each unique year in the dataset. - It automatically creates a sequence that you can play through, showing how the data changes over time.
The result is an interactive visualization with a slider and play controls, perfect for storytelling.
Customizing themes and styling with update_layout()
import plotly.graph_objects as go
fig = go.Figure(data=go.Scatter(x=[1, 2, 3, 4], y=[10, 11, 12, 13]))
fig.update_layout(
template="plotly_dark",
title="Custom styled chart",
xaxis_title="X Axis",
yaxis_title="Y Axis"
)
fig.show()--OUTPUT--[Interactive dark-themed line chart with custom axis labels and title]
The update_layout() method is your main tool for customizing a chart's appearance beyond the data itself. It lets you control everything from themes to titles, giving you fine-grained control over the final look and feel.
- The
templateparameter applies a complete visual style. Setting it to"plotly_dark", for example, instantly gives your chart a dark theme. - You can also set specific labels, such as the main
titleand custom text for thexaxis_titleandyaxis_title.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the Plotly techniques we've explored, Replit Agent can turn them into production-ready applications.
- Build a financial dashboard that uses animated charts to track economic indicators over time, similar to how we used the
animation_frameparameter. - Create a marketing analytics tool that uses subplots with
px.bar()andpx.pie()to compare campaign performance and channel distribution side-by-side. - Deploy a public health tracker that visualizes global data with interactive scatter plots, mapping variables like
gdpPercapandlifeExpjust as we did withpx.scatter().
Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser.
Common errors and challenges
Even with its intuitive design, you might run into a few common hurdles when visualizing your data.
Fixing NaN values in Plotly line charts
One of the most frequent issues is seeing gaps in your line charts. This happens when your dataset contains NaN (Not a Number) values, as Plotly won't draw a line across a missing data point. The fix is to clean your data before plotting. You can use pandas functions like fillna() to replace missing values or dropna() to remove the rows entirely, ensuring a continuous line.
Resolving category order in px.bar() charts
By default, Plotly may not sort the bars in your chart in the order you expect. For example, it might sort months alphabetically instead of chronologically. To enforce a specific sequence, use the category_orders argument in functions like px.bar(). This parameter lets you provide a dictionary specifying the exact order for any categorical axis.
Fixing legend display in multi-trace plots
When you combine multiple traces—like several lines on one chart—you can end up with a cluttered or redundant legend. To control this, you can set showlegend=False when adding a trace that doesn't need its own legend entry. For more complex scenarios, the legendgroup parameter helps you group related traces so they share a single legend item, keeping your visualization clean and easy to read.
Fixing NaN values in Plotly line charts
It's common to see unexpected gaps in your line charts. This isn't a bug—it's how Plotly handles NaN (Not a Number) values. By default, it won't draw a line across a missing point. The following code demonstrates this behavior.
import plotly.express as px
import pandas as pd
import numpy as np
data = {'x': [1, 2, 3, 4, 5],
'y': [10, np.nan, 30, np.nan, 50]}
df = pd.DataFrame(data)
fig = px.line(df, x='x', y='y', title="Line chart with missing values")
fig.show()
This code creates a DataFrame with np.nan values to show what happens when data is missing. Plotly's px.line() function skips over these points, creating visible gaps in the chart. Check the next example for a simple fix.
import plotly.express as px
import pandas as pd
import numpy as np
data = {'x': [1, 2, 3, 4, 5],
'y': [10, np.nan, 30, np.nan, 50]}
df = pd.DataFrame(data)
fig = px.line(df, x='x', y='y', title="Line chart with missing values")
fig.update_traces(connectgaps=False) # Show the line with gaps where NaN values exist
fig.show()
A simple fix for gaps is using the connectgaps parameter inside update_traces(). Setting connectgaps=True will draw a continuous line across NaN values, which is ideal for visualizing trends in incomplete time-series data. The example code explicitly sets connectgaps=False to demonstrate the default behavior of showing gaps. You can toggle this setting to either highlight missing data or present a continuous trend, depending on what your story requires.
Resolving category order in px.bar() charts
You might notice that Plotly doesn't always sort bars in the order you expect. For example, it might arrange months alphabetically instead of chronologically. This happens because px.bar() defaults to alphabetical sorting. The code below demonstrates this common issue.
import plotly.express as px
import pandas as pd
data = {'month': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
'sales': [200, 150, 300, 250, 400]}
df = pd.DataFrame(data)
# Months will be ordered alphabetically, not chronologically
fig = px.bar(df, x='month', y='sales', title="Monthly Sales")
fig.show()
The px.bar() function treats the month column as plain text, sorting it alphabetically instead of chronologically. This creates a jumbled view of your sales data. The next example shows how to enforce the correct order.
import plotly.express as px
import pandas as pd
data = {'month': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
'sales': [200, 150, 300, 250, 400]}
df = pd.DataFrame(data)
# Specify category_orders to maintain correct chronological order
fig = px.bar(df, x='month', y='sales', title="Monthly Sales",
category_orders={"month": ["Jan", "Feb", "Mar", "Apr", "May"]})
fig.show()
To fix the sorting, use the category_orders argument in px.bar(). This parameter takes a dictionary where you map a column name, like "month", to a list specifying the desired order. This tells Plotly exactly how to arrange the bars, overriding the default alphabetical sort. It's essential for any data with a natural but non-alphabetical sequence, such as days of the week or custom stages in a process.
Fixing legend display in multi-trace plots
When you add multiple traces to a chart, the legend can become cluttered, especially if different data series share the same name. This makes it hard to tell them apart. The following code demonstrates what happens when this occurs.
import plotly.graph_objects as go
# Creating two traces with the same name causes legend confusion
fig = go.Figure()
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6], name="Data"))
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[7, 8, 9], name="Data"))
fig.update_layout(title="Multiple traces with same name")
fig.show()
The code assigns the same name to both traces, causing them to share one legend item. This prevents you from toggling each line independently. The next example demonstrates how to resolve this.
import plotly.graph_objects as go
# Creating two traces with unique names
fig = go.Figure()
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6], name="Data Series 1"))
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[7, 8, 9], name="Data Series 2"))
fig.update_layout(title="Multiple traces with unique names")
fig.show()
The solution is to give each trace a unique name. When multiple traces share the same name, Plotly combines them into a single legend item, so you can't toggle them separately. By assigning distinct names like "Data Series 1" and "Data Series 2", each trace gets its own entry. This gives you full control over the legend, which is especially important when you're layering multiple datasets onto one chart for comparison.
Real-world applications
With the fundamentals and common fixes covered, you can now build powerful, domain-specific visualizations for finance and geography.
Analyzing financial data with go.Candlestick()
Candlestick charts are a staple in financial analysis, and Plotly's go.Candlestick() function gives you a powerful way to visualize stock price movements by showing the open, high, low, and close for a specific period.
import plotly.graph_objects as go
# Sample stock data for 5 days
dates = ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04', '2023-01-05']
open_prices = [150, 152, 149, 155, 157]
high_prices = [154, 155, 153, 159, 159]
low_prices = [148, 148, 147, 153, 155]
close_prices = [152, 149, 152, 157, 156]
fig = go.Figure(data=[go.Candlestick(x=dates, open=open_prices, high=high_prices,
low=low_prices, close=close_prices)])
fig.update_layout(title="Stock Price Analysis")
fig.show()
This example uses plotly.graph_objects to construct the visualization from the ground up. It starts by defining separate lists for each data component required by a candlestick chart.
- The
go.Figure()function initializes the chart object. - Inside, a
go.Candlestick()trace is created, mapping each list—likeopen_pricesandhigh_prices—to its corresponding visual parameter.
This declarative approach lets you build complex financial charts by simply providing the right data inputs. The update_layout() method then adds a title before the chart is displayed.
Creating interactive maps with px.choropleth()
Choropleth maps are a powerful way to visualize geographical data, and Plotly's px.choropleth() function lets you create them by coloring regions like states or countries based on data values.
import plotly.express as px
# Load built-in US states data
df = px.data.election()
# Create choropleth map
fig = px.choropleth(
df,
geojson=px.data.election_geojson(),
locations='district',
color='winner',
scope="usa",
color_discrete_map={'Biden': 'blue', 'Trump': 'red'},
hover_data=['Biden', 'Trump']
)
fig.update_layout(title="US Election Results by District")
fig.show()
This example uses px.choropleth() to map data onto geographical regions. It’s powered by two key inputs: a DataFrame with your data and a geojson file that defines the map's shapes.
- The
locationsparameter links each district in your data to its corresponding shape in thegeojson. - The
colorargument fills each region based on the values in the'winner'column. - You can set custom colors using
color_discrete_mapand focus the view withscope="usa".
Finally, hover_data adds extra information to the tooltips, making the map more interactive.
Get started with Replit
Turn your new skills into a real application. Tell Replit Agent to "build a stock tracker with go.Candlestick()" or "create a US election map using px.choropleth()".
Replit Agent writes the code, tests for errors, and deploys your app. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.


%2520function%2520in%2520Python.png)
