How to Create a Funnel Chart in Plotly
• Funnel charts excel at visualizing sequential processes where volume decreases at each stage—perfect for sales pipelines, conversion funnels, and user journey analytics where you need to identify...
Key Insights
• Funnel charts excel at visualizing sequential processes where volume decreases at each stage—perfect for sales pipelines, conversion funnels, and user journey analytics where you need to identify drop-off points quickly.
• Plotly’s go.Funnel() provides superior interactivity compared to static alternatives, with built-in hover effects, zoom capabilities, and easy HTML export for sharing with stakeholders who don’t code.
• The real power comes from customizing connector lines, adding percentage annotations, and creating grouped funnels to compare multiple time periods or segments side-by-side—features that transform basic charts into decision-making tools.
Introduction to Funnel Charts
Funnel charts visualize data that progressively decreases through sequential stages. Unlike bar charts or line graphs, funnels immediately communicate process efficiency and highlight where you’re losing the most value. Sales teams use them to track pipeline progression from leads to closed deals. Product managers analyze user conversion through signup flows. Marketing teams measure campaign effectiveness across awareness, consideration, and purchase stages.
Plotly stands out for funnel visualization because it combines Python’s data manipulation capabilities with interactive, publication-ready graphics. You get hover tooltips, responsive layouts, and seamless integration with web applications—all without writing JavaScript. When you need to present conversion data to executives or embed analytics in dashboards, Plotly delivers professional results with minimal code.
Setting Up Your Environment
Install Plotly using pip. The library has no heavy dependencies and works in Jupyter notebooks, standalone scripts, and web frameworks alike.
pip install plotly pandas
Import the necessary modules. You’ll primarily use plotly.graph_objects for detailed control, though plotly.express offers quick alternatives for simple cases.
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
Verify your installation by checking the version. Plotly 5.x introduced significant performance improvements for large datasets.
import plotly
print(plotly.__version__) # Should be 5.x or higher
Creating a Basic Funnel Chart
Start with a straightforward sales pipeline funnel. Define your stages and corresponding values, then pass them to go.Funnel().
# Sales pipeline data
stages = ['Website Visitors', 'Sign-ups', 'Trial Users', 'Paying Customers', 'Annual Subscribers']
values = [15000, 4500, 1800, 750, 320]
# Create basic funnel
fig = go.Figure(go.Funnel(
y=stages,
x=values,
textinfo="value+percent initial"
))
fig.update_layout(
title="SaaS Conversion Funnel - Q1 2024",
height=500
)
fig.show()
The textinfo parameter controls what appears on each segment. Using “value+percent initial” displays both the absolute number and percentage relative to the first stage. This immediately shows you’re converting 2.1% of visitors to paying customers—a critical metric.
The funnel automatically sizes each segment proportionally. The narrowing visualization makes drop-offs visually obvious. You can instantly see the biggest conversion problem: only 30% of sign-ups become trial users.
Customizing Funnel Appearance
Default styling works for quick analysis, but production dashboards need polish. Customize colors to match your brand, adjust text positioning for readability, and modify connector lines to control visual flow.
# Enhanced funnel with custom styling
fig = go.Figure(go.Funnel(
y=stages,
x=values,
textposition="inside",
textfont=dict(size=14, color="white", family="Arial Black"),
marker=dict(
color=['#3b82f6', '#8b5cf6', '#ec4899', '#f59e0b', '#10b981'],
line=dict(width=2, color='white')
),
connector=dict(
line=dict(color='#6b7280', width=3, dash='dot')
)
))
fig.update_layout(
title=dict(
text="SaaS Conversion Funnel - Q1 2024",
font=dict(size=24, color="#1f2937")
),
height=550,
paper_bgcolor='#f9fafb',
plot_bgcolor='#f9fafb',
margin=dict(l=150, r=150, t=80, b=40)
)
fig.show()
Color gradients guide the eye through the funnel progression. The connector line style creates visual separation between stages without cluttering the chart. Increased margins prevent label cutoff—a common issue when stage names are long.
Text positioning matters more than you’d think. “Inside” placement works when segments are large enough; switch to “auto” for funnels with dramatic drop-offs where later stages become too narrow for readable text.
Advanced Funnel Techniques
Comparing multiple funnels reveals trends that single-period analysis misses. Create grouped funnels to contrast different time periods, user segments, or A/B test variants.
# Quarterly comparison funnel
fig = go.Figure()
# Q1 data
fig.add_trace(go.Funnel(
name='Q1 2024',
y=stages,
x=[15000, 4500, 1800, 750, 320],
textinfo="value+percent previous",
marker=dict(color='#3b82f6')
))
# Q2 data
fig.add_trace(go.Funnel(
name='Q2 2024',
y=stages,
x=[18500, 6200, 2400, 1100, 510],
textinfo="value+percent previous",
marker=dict(color='#10b981')
))
fig.update_layout(
title="Quarterly Conversion Comparison",
height=600,
showlegend=True,
funnelmode='group' # Places funnels side-by-side
)
fig.show()
The funnelmode='group' parameter places funnels adjacently for direct comparison. Changing textinfo to “percent previous” shows stage-to-stage conversion rates rather than overall funnel percentage—often more actionable for identifying specific bottlenecks.
For horizontal funnels, swap the x and y parameters and set orientation. This works better for presentations where screen width exceeds height.
fig = go.Figure(go.Funnel(
x=stages,
y=values,
orientation='h',
textinfo="value+percent initial"
))
Interactive Features and Deployment
Interactivity transforms static charts into exploration tools. Customize hover templates to display additional metrics without cluttering the visual.
# Funnel with rich hover data
conversion_rates = [100, 30, 40, 42, 43] # Stage-to-stage conversion %
revenue = [0, 0, 0, 112500, 102400] # Revenue per stage
fig = go.Figure(go.Funnel(
y=stages,
x=values,
text=values,
textinfo="text+percent initial",
hovertemplate='<b>%{y}</b><br>' +
'Count: %{x:,}<br>' +
'Conversion Rate: %{customdata[0]:.1f}%<br>' +
'Revenue: $%{customdata[1]:,}<br>' +
'<extra></extra>',
customdata=list(zip(conversion_rates, revenue)),
marker=dict(color=['#3b82f6', '#8b5cf6', '#ec4899', '#f59e0b', '#10b981'])
))
fig.update_layout(
title="Revenue-Attributed Conversion Funnel",
height=550
)
# Save as interactive HTML
fig.write_html("conversion_funnel.html")
# Export static image (requires kaleido)
# fig.write_image("funnel.png", width=1200, height=800)
fig.show()
The customdata parameter lets you attach arbitrary data to each segment. Combined with custom hover templates, you can surface revenue, dates, or any metric relevant to your analysis. The <extra></extra> tag removes the default trace name from hover tooltips for cleaner presentation.
Exporting to HTML creates standalone files that work in any browser—no Python required. Share them via email, embed in Notion or Confluence, or host on internal wikis. For static exports, install kaleido (pip install kaleido) to generate PNG or SVG files for presentations.
Real-World Application
Here’s a complete e-commerce checkout funnel using pandas for data manipulation. This pattern integrates with existing data pipelines.
import pandas as pd
import plotly.graph_objects as go
# Simulate realistic e-commerce data
data = {
'stage': ['Product Views', 'Add to Cart', 'Checkout Initiated',
'Payment Info', 'Order Complete'],
'users': [45000, 12500, 8200, 6800, 5950],
'mobile_users': [28000, 7800, 4900, 3800, 3200],
'desktop_users': [17000, 4700, 3300, 3000, 2750]
}
df = pd.DataFrame(data)
# Calculate conversion rates
df['conversion_rate'] = (df['users'] / df['users'].iloc[0] * 100).round(1)
df['stage_drop'] = df['users'].diff().fillna(0).abs().astype(int)
# Create funnel with calculated metrics
fig = go.Figure(go.Funnel(
y=df['stage'],
x=df['users'],
textinfo="value",
hovertemplate='<b>%{y}</b><br>' +
'Total Users: %{x:,}<br>' +
'Conversion Rate: %{customdata[0]:.1f}%<br>' +
'Drop-off: %{customdata[1]:,}<br>' +
'<extra></extra>',
customdata=df[['conversion_rate', 'stage_drop']].values,
marker=dict(
color=df['conversion_rate'],
colorscale='RdYlGn',
showscale=True,
colorbar=dict(title="Conversion %")
)
))
fig.update_layout(
title="E-commerce Checkout Funnel - Last 30 Days",
height=600,
annotations=[
dict(
text=f"Overall Conversion: {df['conversion_rate'].iloc[-1]}%",
xref="paper", yref="paper",
x=0.5, y=1.08, showarrow=False,
font=dict(size=16, color='#1f2937')
)
]
)
fig.show()
# Identify biggest drop-off
max_drop_idx = df['stage_drop'].idxmax()
print(f"Biggest drop-off: {df.loc[max_drop_idx, 'stage']} "
f"(-{df.loc[max_drop_idx, 'stage_drop']:,} users)")
This example demonstrates production-ready patterns: loading data from DataFrames, calculating derived metrics, using color scales to encode additional dimensions, and programmatically identifying problem areas. The color scale immediately highlights which stages have healthy conversion rates (green) versus problematic ones (red).
Funnel charts aren’t just visualizations—they’re diagnostic tools. Build them into your analytics workflow, customize them for your specific metrics, and let Plotly’s interactivity help stakeholders explore the data themselves. The combination of visual clarity and technical depth makes funnels indispensable for any process-driven analysis.