KQ-1313 · FUNNEL/FLOW

Funnel Flow

A conversion funnel drawn as a flow: stages are bars as long as the people who reached them, ribbons carry the ones who went on, and from each stage a drop-off branch peels away and fades. A seeded pool of particles runs it, and at each stage a particle goes on only if its lane falls inside the share that converted, so the dots split exactly as the people did; pointing at a stage freezes its particles where they are while the rest keep flowing and the details swap in on snap, and the segment switch re-weights every stage and branch on glide while particles already in flight follow their lanes onto the new shapes. The stages are a list of buttons named with full sentences: arrows move between them and freeze each, Enter pins, Escape unpins, and the segment is a radio group.

KQ-1313 · FUNNEL/FLOW

Trial funnel

Fieldline · September · 48,200 visited

2.3% end to endReached the end1,130 of 48,200Most lost at Activated62.2%

Point at a stage to freeze it; press to pin.

all segments · 2.3% end to end · most lost at activated

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/funnel-flow

Props

particles
number
default 100
How many particles run the funnel at once, 20 to 200. The pool is seeded and warmed up, so it is full from the first frame.
segment / defaultSegment
string
default the first segment
Which of segments the flow is weighted by. A change glides every stage and branch to its new size. Controlled or uncontrolled.
onSegmentChange
(id: string) => void
default —
Fires from the segment switch with the new segment's id.
speed
number
default 1
How fast the particles run, as a multiple of 64px a second.
stages
FunnelStage[]
default defaultFunnelStages
The stages in order, each an id and a label.
segments
FunnelSegment[]
default defaultFunnelSegments
The segments: id, label, the people who reached each stage, and optionally the median hours from the stage before and the top reason people left at each stage.
stage / defaultStage
string | null
default null
The pinned stage: frozen, with its details shown, until it is unpinned. Controlled or uncontrolled.
onStageChange
(id: string | null) => void
default —
Fires from the press or key that pinned or unpinned a stage.
seed
number
default 7
Seeds the particles' lanes and pace, so the same page always draws the same flow.
format
(count: number) => string
default —
Formats a count of people. Defaults to grouped digits in locale.
locale
string
default "en-US"
The locale for counts.
status
"ready" | "loading" | "error"
default "ready"
Whether the numbers have arrived: loading shows the funnel's skeleton, error a retry.
onRetry
() => void
default —
Try again was pressed after the numbers failed to load.
title
string
default "Conversion"
The heading.
subtitle
string
default —
A quieter line under the heading, before the first stage's count.
label
string
default —
The funnel's accessible name. Defaults to the title.
sound
boolean
default false
Play a swish when the segment changes and a plip when a stage is pinned or the keyboard moves to it, pitched by how many of its people the stage keeps. Particles never sound.
disabled
boolean
default false
Shows the flow but refuses segment changes and pins.
className
string
default —
Classes for the outer panel.