How to Create a Line Chart in Plotly

Line charts are the workhorse of time series visualization, and Plotly handles them exceptionally well. Unlike matplotlib or seaborn, Plotly generates interactive JavaScript-based visualizations that...

Key Insights

  • Plotly offers two approaches for line charts: plotly.express for quick visualizations and plotly.graph_objects for granular control—choose based on your customization needs
  • Interactive features like zoom, pan, and hover tooltips come built-in with zero configuration, making Plotly superior to static plotting libraries for exploratory data analysis
  • Multi-line charts require careful attention to color schemes and legends to remain readable—limit to 5-7 lines maximum and use distinct colors from Plotly’s qualitative palettes

Introduction to Plotly Line Charts

Line charts are the workhorse of time series visualization, and Plotly handles them exceptionally well. Unlike matplotlib or seaborn, Plotly generates interactive JavaScript-based visualizations that work seamlessly in Jupyter notebooks, web applications, and standalone HTML files.

Plotly provides two APIs for creating line charts. plotly.express offers a high-level, declarative interface perfect for rapid prototyping and standard visualizations. plotly.graph_objects gives you low-level control over every visual element when you need pixel-perfect customization. Most projects start with express and migrate specific charts to graph_objects only when necessary.

Use line charts when you need to show trends over continuous data—typically time series like stock prices, web traffic, or sensor readings. They excel at revealing patterns, seasonality, and anomalies that table formats obscure.

Basic Line Chart Setup

Install Plotly with pip if you haven’t already:

pip install plotly pandas

Here’s a minimal line chart showing monthly revenue data:

import plotly.express as px
import pandas as pd

# Sample data
data = {
    'month': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    'revenue': [45000, 52000, 48000, 61000, 58000, 67000]
}
df = pd.DataFrame(data)

# Create line chart
fig = px.line(df, x='month', y='revenue', 
              title='Monthly Revenue 2024')
fig.show()

This generates an interactive chart with hover tooltips, zoom controls, and a modebar for exporting. The fig.show() method opens the chart in your browser or displays it inline in Jupyter notebooks.

For programmatic control without DataFrames, use lists directly:

months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
revenue = [45000, 52000, 48000, 61000, 58000, 67000]

fig = px.line(x=months, y=revenue, title='Monthly Revenue 2024')
fig.show()

Customizing Line Appearance

Default styling works for quick analysis, but production visualizations need customization. Control line properties through the update_traces() method:

import plotly.graph_objects as go

# Create figure with graph_objects for more control
fig = go.Figure()

# Add multiple lines with different styles
fig.add_trace(go.Scatter(
    x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    y=[45000, 52000, 48000, 61000, 58000, 67000],
    mode='lines+markers',
    name='Revenue',
    line=dict(color='#2E86AB', width=3),
    marker=dict(size=8, symbol='circle')
))

fig.add_trace(go.Scatter(
    x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    y=[38000, 41000, 39000, 49000, 47000, 54000],
    mode='lines+markers',
    name='Costs',
    line=dict(color='#A23B72', width=3, dash='dash'),
    marker=dict(size=8, symbol='diamond')
))

fig.update_layout(
    title='Revenue vs Costs',
    xaxis_title='Month',
    yaxis_title='Amount ($)',
    hovermode='x unified'
)

fig.show()

Key styling parameters:

  • line.color: Hex codes, RGB, or named colors
  • line.width: Thickness in pixels (2-4 works well)
  • line.dash: ‘solid’, ‘dot’, ‘dash’, ’longdash’, ‘dashdot’
  • marker.symbol: Over 40 options including ‘circle’, ‘square’, ‘diamond’, ‘cross’
  • mode: ’lines’, ‘markers’, or ’lines+markers’

The hovermode='x unified' parameter creates a single hover label for all lines at the same x-position—much cleaner than individual tooltips.

Working with Multiple Lines

Multi-line charts compare related metrics. With plotly.express, pass a DataFrame in long format and specify the color parameter:

import plotly.express as px
import pandas as pd

# Data in long format
data = {
    'month': ['Jan', 'Feb', 'Mar', 'Apr'] * 3,
    'value': [45, 52, 48, 61, 38, 41, 39, 49, 12, 15, 14, 18],
    'metric': ['Revenue']*4 + ['Costs']*4 + ['Profit']*4
}
df = pd.DataFrame(data)

fig = px.line(df, x='month', y='value', color='metric',
              title='Business Metrics Q1 2024',
              labels={'value': 'Amount ($K)', 'metric': 'Metric'},
              color_discrete_sequence=['#2E86AB', '#A23B72', '#F18F01'])

fig.update_traces(mode='lines+markers', line=dict(width=3))
fig.update_layout(hovermode='x unified', legend=dict(
    orientation="h",
    yanchor="bottom",
    y=1.02,
    xanchor="right",
    x=1
))

fig.show()

For wide-format data (columns as series), use:

df_wide = pd.DataFrame({
    'month': ['Jan', 'Feb', 'Mar', 'Apr'],
    'Revenue': [45, 52, 48, 61],
    'Costs': [38, 41, 39, 49],
    'Profit': [12, 15, 14, 18]
})

fig = px.line(df_wide, x='month', y=['Revenue', 'Costs', 'Profit'])
fig.show()

Legend positioning matters. Horizontal legends above the chart (orientation="h") work better than vertical side legends when you have 3+ lines.

Advanced Formatting Options

Production-ready charts need polished formatting. Here’s a comprehensive example:

import plotly.graph_objects as go
from datetime import datetime, timedelta

# Generate time series data
dates = [datetime(2024, 1, 1) + timedelta(days=x) for x in range(90)]
values = [100 + x*0.5 + (x%10)*2 for x in range(90)]

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=dates,
    y=values,
    mode='lines',
    name='Daily Active Users',
    line=dict(color='#2E86AB', width=2),
    hovertemplate='<b>Date</b>: %{x|%B %d}<br>' +
                  '<b>Users</b>: %{y:,.0f}<br>' +
                  '<extra></extra>'
))

# Add annotation for significant event
fig.add_annotation(
    x=datetime(2024, 2, 15),
    y=125,
    text="Marketing Campaign Launch",
    showarrow=True,
    arrowhead=2,
    arrowsize=1,
    arrowwidth=2,
    arrowcolor="#FF6B35",
    ax=-80,
    ay=-40
)

fig.update_layout(
    title={
        'text': 'User Growth Q1 2024',
        'x': 0.5,
        'xanchor': 'center',
        'font': {'size': 24}
    },
    xaxis=dict(
        title='Date',
        showgrid=True,
        gridwidth=1,
        gridcolor='LightGray',
        range=[datetime(2024, 1, 1), datetime(2024, 4, 1)]
    ),
    yaxis=dict(
        title='Daily Active Users',
        showgrid=True,
        gridwidth=1,
        gridcolor='LightGray',
        range=[90, 160]
    ),
    plot_bgcolor='white',
    hovermode='x'
)

fig.show()

The hovertemplate parameter controls tooltip formatting. Use %{x} and %{y} for values, with format specifiers like %{y:,.0f} for comma-separated integers. The <extra></extra> tag removes the trace name from the tooltip.

Interactive Features

Plotly’s interactivity requires no JavaScript. Add a range slider for time series navigation:

import plotly.graph_objects as go
import numpy as np

# Generate sample data
days = np.arange(365)
values = 100 + np.cumsum(np.random.randn(365) * 2)

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=days,
    y=values,
    mode='lines',
    name='Stock Price',
    line=dict(color='#2E86AB', width=2)
))

fig.update_xaxes(
    rangeslider_visible=True,
    rangeselector=dict(
        buttons=list([
            dict(count=1, label="1m", step="month", stepmode="backward"),
            dict(count=3, label="3m", step="month", stepmode="backward"),
            dict(count=6, label="6m", step="month", stepmode="backward"),
            dict(step="all", label="All")
        ])
    )
)

fig.update_layout(
    title='Stock Price with Range Selector',
    xaxis_title='Days',
    yaxis_title='Price ($)',
    hovermode='x'
)

fig.show()

The range slider appears below the main chart. Range selector buttons provide quick navigation to common time periods. Users can also drag the slider handles or click-drag on the main chart to zoom.

Custom hover data displays additional context:

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=[1, 2, 3, 4, 5],
    y=[10, 15, 13, 17, 20],
    mode='lines+markers',
    customdata=[[500], [750], [600], [850], [1000]],
    hovertemplate='<b>Week %{x}</b><br>' +
                  'Sales: $%{y}K<br>' +
                  'Orders: %{customdata[0]}<br>' +
                  '<extra></extra>'
))

fig.show()

Exporting and Integration

Save charts as standalone HTML files:

fig.write_html("revenue_chart.html")

This creates a self-contained file with embedded JavaScript—perfect for sharing or embedding in web applications.

For static images (requires kaleido package):

# pip install kaleido
fig.write_image("chart.png", width=1200, height=600)
fig.write_image("chart.pdf")

In Jupyter notebooks, charts display automatically with fig.show(). For Dash web applications, return the figure object directly:

import dash
from dash import dcc, html

app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Graph(figure=fig)
])

if __name__ == '__main__':
    app.run_server(debug=True)

Plotly integrates with Streamlit, Flask, Django, and FastAPI. The HTML export works universally—embed it in any webpage with an iframe.

Line charts in Plotly strike the right balance between ease of use and customization depth. Start with plotly.express for prototypes, add styling through update_layout() and update_traces(), and switch to graph_objects only when you need absolute control. The interactive features work out of the box, making your visualizations immediately more useful than static alternatives.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.