Funnel charts display values as progressively decreasing (or increasing) slices, ideal for visualizing stages in a process, conversion rates, or hierarchical data.
When to Use Funnel Charts
Use funnel charts when you need to:
- Visualize conversion funnels (sales, marketing)
- Show progressive reduction through stages
- Display hierarchical data with pyramids
- Illustrate filtering or selection processes
- Track user journey through stages
- Show population demographics
Creating a Funnel Chart
Here’s a complete example:
import * as am5 from "@amcharts/amcharts5";
import * as am5percent from "@amcharts/amcharts5/percent";
// Create root element
const root = am5.Root.new("chartdiv");
// Create chart
const chart = root.container.children.push(
am5percent.SlicedChart.new(root, {
layout: root.verticalLayout
})
);
// Create funnel series
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
orientation: "vertical",
valueField: "value",
categoryField: "category",
bottomRatio: 0,
alignLabels: false
})
);
// Configure slices
series.slices.template.setAll({
strokeWidth: 2,
stroke: am5.color(0xffffff)
});
// Configure labels
series.labels.template.setAll({
text: "{category}: {value}",
fontSize: 14,
fill: am5.color(0xffffff)
});
// Set data
series.data.setAll([
{ category: "Website Visits", value: 10000 },
{ category: "Product Views", value: 5000 },
{ category: "Add to Cart", value: 2000 },
{ category: "Checkout", value: 1000 },
{ category: "Purchase", value: 500 }
]);
// Add legend
const legend = chart.children.push(
am5.Legend.new(root, {
centerX: am5.percent(50),
x: am5.percent(50),
marginTop: 15
})
);
legend.data.setAll(series.dataItems);
Configuration Options
Orientation
Funnel charts can be displayed vertically or horizontally.
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
orientation: "horizontal" // or "vertical"
})
);
Bottom Ratio
Controls the width of the bottom edge of each slice:
- bottomRatio:
0 - Trapezoid shape (default for funnel)
- bottomRatio:
1 - Rectangle shape
- bottomRatio: Values between 0-1 for intermediate shapes
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
bottomRatio: 0 // Full funnel effect
})
);
Start and End Locations
Control where the funnel starts and ends:
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
startLocation: 0, // 0 = beginning
endLocation: 1 // 1 = end
})
);
Creating a Pyramid Chart
import * as am5percent from "@amcharts/amcharts5/percent";
const series = chart.series.push(
am5percent.PyramidSeries.new(root, {
orientation: "vertical",
valueField: "value",
categoryField: "category"
})
);
series.data.setAll([
{ category: "0-10", value: 1000 },
{ category: "10-20", value: 2000 },
{ category: "20-30", value: 3000 },
{ category: "30-40", value: 2500 },
{ category: "40-50", value: 2000 }
]);
Slice Configuration
Appearance
// Configure all slices
series.slices.template.setAll({
strokeWidth: 3,
stroke: am5.color(0xffffff),
cornerRadius: 5,
fillOpacity: 0.9
});
// Hover effect
series.slices.template.states.create("hover", {
scale: 1.02,
fillOpacity: 1
});
Colors
// Use color set
series.set("colors", am5.ColorSet.new(root, {}));
// Custom colors in data
const data = [
{ category: "Stage 1", value: 1000, fill: am5.color(0x3498db) },
{ category: "Stage 2", value: 800, fill: am5.color(0x2ecc71) },
{ category: "Stage 3", value: 600, fill: am5.color(0xf39c12) }
];
Labels and Ticks
Aligned Labels
Align labels in columns/rows outside the funnel:
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
alignLabels: true
})
);
// Configure label appearance
series.labels.template.setAll({
text: "{category}",
fontSize: 12,
fill: am5.color(0x000000)
});
Ticks
// Configure ticks
series.ticks.template.setAll({
stroke: am5.color(0x000000),
strokeWidth: 1,
location: 0.5
});
Inside Labels
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
alignLabels: false
})
);
series.labels.template.setAll({
text: "{category}: {valuePercentTotal.formatNumber('0.00')}%",
fill: am5.color(0xffffff),
fontSize: 14
});
Slice Links
Funnel charts include connectors between slices:
// Configure links
series.links.template.setAll({
fillOpacity: 0.3,
strokeWidth: 0
});
// Hide links for rectangular funnels
series.set("bottomRatio", 1); // Links become invisible
series.slices.template.set("tooltip", am5.Tooltip.new(root, {
labelText: "{category}\nValue: {value}\nPercent: {valuePercentTotal.formatNumber('0.00')}%"
}));
Interactive Features
Click Events
series.slices.template.events.on("click", function(ev) {
const dataItem = ev.target.dataItem;
const category = dataItem?.get("category");
const value = dataItem?.get("value");
const percent = dataItem?.get("valuePercentTotal");
console.log(`${category}: ${value} (${percent?.toFixed(2)}%)`);
});
Active State
// Create active state
const activeState = series.slices.template.states.create("active", {
shiftRadius: 10
});
// Toggle on click
series.slices.template.events.on("click", function(ev) {
const slice = ev.target;
if (slice.isActive()) {
slice.states.applyAnimate("default");
} else {
slice.states.applyAnimate("active");
}
});
Ignoring Zero Values
const series = chart.series.push(
am5percent.FunnelSeries.new(root, {
ignoreZeroValues: true
})
);
Pictorial Stacked Series
Create funnel-like shapes with custom SVG paths:
import * as am5percent from "@amcharts/amcharts5/percent";
const series = chart.series.push(
am5percent.PictorialStackedSeries.new(root, {
orientation: "vertical",
valueField: "value",
categoryField: "category"
})
);
// Set custom SVG path
series.set("svgPath", "M 0,0 L 100,0 L 100,100 L 0,100 Z");
Conversion Rate Calculation
series.labels.template.adapters.add("text", function(text, target) {
const dataItem = target.dataItem;
if (dataItem) {
const index = dataItem.get("index") || 0;
if (index > 0) {
const dataItems = series.dataItems;
const currentValue = dataItem.get("value") || 0;
const previousValue = dataItems[index - 1]?.get("value") || 1;
const conversion = (currentValue / previousValue * 100).toFixed(1);
return `{category}\n{value} (${conversion}%)`;
}
}
return "{category}\n{value}";
});
Legend
const legend = chart.children.push(
am5.Legend.new(root, {
centerX: am5.percent(50),
x: am5.percent(50),
marginTop: 15,
marginBottom: 15,
layout: root.horizontalLayout
})
);
legend.data.setAll(series.dataItems);
// Show values in legend
legend.valueLabels.template.setAll({
text: "{value}"
});
Key Classes
FunnelSeries - Funnel series from @amcharts/amcharts5/percent
PyramidSeries - Pyramid series
PictorialStackedSeries - Custom shape stacked series
SlicedChart - Container for sliced series
FunnelSlice - Individual funnel slice
Use alignLabels: true for funnels with many stages to keep labels readable. Use alignLabels: false for fewer stages to place labels inside slices.
Data should be ordered from largest to smallest value for a proper funnel visualization. The chart does not automatically sort data.