Funnel chart
Updated
FunnelChart draws one centred band per row, top to bottom, its width the value and its ink lighter stage by stage, with each step's conversion between the bands. Like every DitherCharts chart, it fills its container's width, animates in, moves a cursor with the arrow keys, and keeps a data table for screen readers.
Example
Funnel
A sign-up funnel, each stage narrower and lighter
| stage | Users | Step | Of first |
|---|---|---|---|
| Visits | 12000 | - | 100% |
| Sign-ups | 4800 | 40% | 40% |
| Activated | 2900 | 60% | 24% |
| Trials | 1300 | 45% | 11% |
| Paid | 410 | 32% | 3% |
import { FunnelChart } from "@/components/dithercharts/funnel-chart"
import type { ChartConfig } from "@/components/dithercharts/chart-config"
const chartData = [
{ stage: "Visits", users: 12000 },
{ stage: "Sign-ups", users: 4800 },
{ stage: "Activated", users: 2900 },
{ stage: "Trials", users: 1300 },
{ stage: "Paid", users: 410 },
]
const chartConfig = {
users: { label: "Users" },
} satisfies ChartConfig
export function ChartFunnel() {
return (
<FunnelChart
data={chartData}
config={chartConfig}
nameKey="stage"
dataKey="users"
label="Sign-up funnel"
/>
)
}
Install
Charts install with DitherCharts Pro. See Installation to log in and import the stylesheet.
npx dithercharts add funnel-chart
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| datarequired | Type readonly ChartRow[] | Default – | One row per stage, top to bottom, e.g. { stage: "Visits", users: 12000 }; a missing value counts as 0. |
| configrequired | Type ChartConfig | Default – | An entry for dataKey: the value's label. |
| nameKeyrequired | Type string | Default – | The row key that names each stage, e.g. "stage". |
| dataKeyrequired | Type string | Default – | The row key that holds each stage's value, its band's width, e.g. "users". |
| labelrequired | Type string | Default – | Accessible name, e.g. "Sign-up funnel". Also captions the table twin. |
| labels | Type boolean | Default true | Each band's name and value, and each step's conversion between bands. |
| tooltip | Type TooltipOptions | Default – | The tooltip's label, and the stage it opens on at rest. |
| style | Type StyleId | Default "bayer4" | Dither style. |
| animation | Type "develop" | "none" | Default "develop" | Entrance animation; reduced motion always shows the final frame. |
| height | Type number | Default 300 | Height in CSS pixels; the width fills the container. |
| cell | Type number | Default 3 | Dither cell size in CSS pixels. |
| className | Type string | Default – | Classes on the chart's outer figure. |
Styles
The dither styles style takes:
"bayer2""bayer4""bayer8""blue-noise""halftone""crosshatch""pixel""stipple""engraved""mezzotint""woodcut""floyd-steinberg""atkinson""riemersma"
Keyboard and accessibility
The chart is one Tab stop. Focusing it from the keyboard puts the cursor on the first stage, or where the keys last left it; moving focus away clears it. The keys stop at the ends.
| Key | Action |
|---|---|
| Up arrow Down arrow | Previous / next stage |
| Home End | First / last stage |
| Escape | Dismiss the cursor |
Screen readers
- The chart's label names it, says how many stages it has and lists these keys.
- Each key press is announced in a polite live region. Moving the pointer is silent.
- The
tablebutton under the chart shows its data as a table. Screen readers can reach the table even while it's hidden; clicking a row moves the cursor there and announces it. - With
legend, each entry is a toggle button. The last one showing can't be hidden, but its button stays focusable.
On a touch screen, a tap pins the cursor and a tap outside the chart dismisses it.

