How to use Plotly in Python

Your guide to Plotly in Python. Learn different methods, get tips and tricks, see real-world examples, and debug common errors.

How to use Plotly in Python
Published on: 
Tue
Mar 10, 2026
Updated on: 
Wed
Apr 1, 2026
The Replit Team

Plotly is a powerful Python library to create interactive, publication-quality graphs. You can build complex visualizations like heatmaps and 3D plots with just a few lines of code.

In this article, you'll explore key techniques and tips. You'll also find real-world applications and advice to debug your code, which will help you master Plotly for your projects.

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, a high-level wrapper for Plotly that simplifies chart creation. It lets you build complex plots with minimal code by working directly with data frames.

The key function is px.line(), which automatically maps data columns to visual elements. Here, it's configured to:

  • Use the year column for the x-axis.
  • Use the lifeExp column for the y-axis.
  • Set the chart's title.

This declarative approach means you tell Plotly what to visualize, not how to draw it, making your code more readable and concise.

Essential Plotly visualization techniques

Building on the px.line() example, you can apply similar principles to create other fundamental plots like bar charts, scatter plots, and pie charts.

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]

Just like with the line chart, px.bar() maps data columns to visual properties. The code first filters the Gapminder dataset for 2007, then isolates the 10 countries with the highest GDP per capita. The function then plots this slice of data.

  • The x and y arguments assign the country and gdpPercap columns to the axes.
  • The color="continent" parameter is a key addition. It automatically groups and colors the bars by continent, making the chart more informative.

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 great for exploring relationships between variables. This example shows how you can encode multiple data dimensions into a single chart, making it incredibly information-dense.

  • The size="pop" parameter dynamically adjusts each point's size based on population.
  • Setting hover_name="country" adds interactivity, displaying the country's name when you hover over a point.
  • log_x=True applies a logarithmic scale to the x-axis, which is useful for spreading out clustered data points.

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]

The px.pie() function is perfect for visualizing how a whole is divided. In this case, it shows the population distribution across European countries. The function assigns slice sizes based on the values argument ("pop") and labels them using the names argument ("country").

  • You can fine-tune the chart's appearance with fig.update_traces().
  • Setting textinfo to 'percent+label' makes the chart self-explanatory by displaying both the percentage and the country name directly on each slice.

Advanced Plotly features

With the fundamentals covered, you can now create more complex data stories using subplots, animations, and custom styling with functions like update_layout().

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]

Subplots let you display multiple charts in a single figure. This example switches to Plotly's graph_objects module, which gives you finer control than plotly.express. You start by creating a grid using make_subplots().

  • The make_subplots(rows=1, cols=2) function initializes a figure with a grid of one row and two columns.
  • You then add individual charts, called traces, to specific grid positions using fig.add_trace().
  • The row and col arguments specify exactly where each trace should appear in your layout.

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 extra parameter. The key is animation_frame, which tells Plotly which column to use for sequencing the animation.

  • Setting animation_frame="year" instructs Plotly to generate a separate scatter plot for each year in the dataset.
  • It then stitches these frames together into a smooth animation, complete with a slider and play/pause controls, letting you watch the data evolve over time.

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() function is your main tool for styling the figure's layout, controlling everything from themes to titles. It modifies the non-data components of your chart, giving you precise control over its final appearance.

  • The template argument, set here to plotly_dark, applies a complete visual theme with one line.
  • You can also set specific elements like the chart's title and axis labels (xaxis_title, yaxis_title) for clarity.

Move faster with Replit

Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. Describe what you want to build, and Agent 4 handles everything—from writing the code to connecting databases and deploying it live.

Instead of piecing together techniques, describe the app you actually want to build and the Agent will take it from idea to working product:

  • A dynamic dashboard that visualizes market data with interactive bar and line charts.
  • An analytics tool that generates animated scatter plots to show how trends evolve over time.
  • A reporting utility that takes raw data and displays it as a clean pie chart showing population breakdowns by continent.

Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

Even with a powerful tool like Plotly, you'll likely run into a few common snags, but most have straightforward fixes.

Fixing NaN values in Plotly line charts

One frequent issue is getting disconnected lines or gaps in your line charts. This usually happens when your dataset contains NaN (Not a Number) values. Plotly won't plot these points, breaking the line. To fix this, you need to handle the missing data before plotting. You can use pandas functions like dropna() to remove the rows with missing values entirely or fillna() to replace them with a specific value, ensuring a continuous line.

Resolving category order in px.bar() charts

You might notice that px.bar() sometimes sorts your bars alphabetically instead of in a meaningful order you intended. To control the sequence, you can use the category_orders argument. By passing a dictionary that maps a column name to a list of its desired order—for example, category_orders={"month": ["Jan", "Feb", "Mar"]}—you can force the chart to display the bars exactly how you want them.

Fixing legend display in multi-trace plots

When working with subplots or multiple traces, legends can become cluttered or show duplicate entries. You can clean this up by explicitly naming each trace with the name parameter when using go.add_trace(). For finer control, you can also selectively hide entries from the legend by targeting a specific trace and setting showlegend=False with the update_traces() function. This keeps your final visualization clean and easy to interpret.

Fixing NaN values in Plotly line charts

One of the most frequent snags with line charts is getting disconnected lines. This usually means your dataset has NaN (Not a Number) values. Plotly skips these points, creating gaps. The code below shows exactly what this looks like.

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()

The np.nan values in the y column cause px.line() to skip drawing those data points, creating visible gaps in the chart. The following code demonstrates a common way to resolve this issue.

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()

The code uses fig.update_traces(connectgaps=False) to control how Plotly handles missing data. This setting explicitly leaves gaps where NaN values exist. While you could set it to True to connect the line, it's often better to show the gaps because it visually alerts you to missing information. This ensures your chart accurately reflects the underlying data, preventing a misleading continuous line.

Resolving category order in px.bar() charts

It’s a common snag: px.bar() often sorts categories alphabetically by default, not in their logical order. This can make charts with sequential data, like months, confusing to read. The following code shows exactly what this looks like in practice.

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 code passes the months as simple strings, so px.bar() defaults to alphabetical sorting. This jumbles the chart’s timeline, making it hard to read. The next example demonstrates how to set the intended 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()

The fix is to explicitly set the bar order using the category_orders argument. It's crucial whenever your data has a natural sequence, like months or weekdays, that isn't alphabetical.

  • You pass a dictionary where the key is the column name (e.g., "month").
  • The value is a list containing the categories in your desired order.

This forces Plotly to follow your sequence, making the chart logical and easy to read.

Fixing legend display in multi-trace plots

When you're working with subplots or multiple traces, legends can quickly become cluttered or show duplicate entries. This often happens when you add several traces using go.add_trace() that share the same name. The code below shows how this creates a confusing legend.

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()

Because both go.add_trace() calls use the same name="Data", Plotly generates a redundant legend, making it impossible to tell the lines apart. The following code demonstrates how to give each trace a unique identity.

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()

By assigning a distinct name to every trace added with go.add_trace(), you ensure the legend clearly distinguishes between different data series. This gives each line a unique label, like "Data Series 1", making the chart easy to read.

  • This is crucial when building figures with multiple data series, especially in dashboards or comparative plots.
  • Without unique names, your legend becomes ambiguous and your visualization loses clarity.

Real-world applications

Now that you can troubleshoot common issues, you can confidently apply these techniques to real-world scenarios like financial analysis and geographic mapping.

Analyzing financial data with go.Candlestick()

The go.Candlestick() function is purpose-built for financial analysis, letting you visualize a stock's open, high, low, and close prices in a single, intuitive chart.

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 code uses Plotly's graph_objects module to build a specialized financial chart. It first organizes the stock data—dates, open, high, low, and close prices—into separate lists. The core of the visualization is the go.Candlestick() function, which maps this data to the chart's visual elements.

  • The x parameter sets the dates on the horizontal axis.
  • The open, high, low, and close parameters define the body and wicks of each candlestick.

This structure gives you a clear view of price movements for each day.

Creating interactive maps with px.choropleth()

The px.choropleth() function lets you create interactive maps that color geographical regions based on data values, making it perfect for visualizing location-based information.

This code visualizes US election results by using Plotly’s built-in election dataset and a corresponding geojson file, which defines the shapes of the congressional districts. The px.choropleth() function then ties this data to the map's visual properties.

  • The locations parameter links each row in your data to a specific shape in the geojson file.
  • The color argument colors each district based on the data in the winner column.
  • Setting scope="usa" automatically zooms the map to the United States.
  • You can use color_discrete_map to assign specific colors—in this case, blue for 'Biden' and red for 'Trump'.

This creates a clear, interactive map where you can hover over each district to see detailed results.

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 code creates a choropleth map using Plotly’s convenient built-in datasets, px.data.election() and its corresponding px.data.election_geojson() file. This approach lets you build a complex map without needing to find and load external data. The main function is px.choropleth().

  • A key feature for interactivity is the hover_data argument. It specifies what additional data appears in the tooltip when you hover over a district.
  • The final line uses update_layout() to add a descriptive title, making the chart's purpose clear at a glance.

Get started with Replit

Now, turn your knowledge into a real tool. Tell Replit Agent to "build a stock tracker with go.Candlestick()" or "create a US population map using px.choropleth()".

It handles writing the code, testing for errors, and deploying your app from a simple prompt. Start building with Replit.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.

Get started for free

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.