Funnel
funnelVisualizes stage-by-stage quantities as a proportional conversion funnel.
Usage
Basic usage (vertical)
One line per level, the center bar scales the width according to the value ratio, and the conversion rate logo is displayed by default between levels. per-stage tone Eat token.
<Funnel
stages={[
{ id: "in", label: "Influx", value: 1240, tone: "brand" },
{ id: "route", label: "Routing", value: 1080, tone: "brand" },
{ id: "exec", label: "Execute", value: 860, tone: "warning" },
{ id: "done", label: "Complete", value: 720, tone: "success" },
]}
ariaLabel="Funnel chart"
conversionLabel="Conversion"
/>Horizontal (horizontal)
One stage per column, scaling the height according to value, suitable for conversion funnels.
<Funnel
stages={[
{ id: "visit", label: "Access", value: 8600, tone: "brand" },
{ id: "signup", label: "Registration", value: 4200, tone: "brand" },
{ id: "trial", label: "Trial", value: 1900, tone: "warning" },
{ id: "pay", label: "Payment", value: 640, tone: "success" },
]}
orientation="horizontal"
ariaLabel="Funnel chart"
conversionLabel="Conversion"
/>Hide conversion rate logo
showConversion={false} Turn off the inter-stage conversion rate and only look at the volume.
<Funnel
stages={stages}
showConversion={false}
ariaLabel="Funnel chart"
conversionLabel="Conversion"
/>Click to drill down
After passing onStageClick, the bar changes to a button. Click on the takeback stage to drill down.
<Funnel
stages={stages}
ariaLabel="Funnel chart"
conversionLabel="Conversion"
onStageClick={(s) => console.log(s.id)}
/>When to use
Use Funnel for attrition in one linear process, such as visit to registration to payment. Use Sankey for branching directed flows, or [Table] for flat values.
Import
import { Funnel, computeFunnel } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| stages* | FunnelStage[] | — | {id, label, value, tone?} stages sized proportionally to value. |
| orientation | "vertical" | "horizontal" | "vertical" | Rows sized by width or columns sized by height. |
| showConversion | boolean | true | Shows conversion between adjacent stages. |
| ariaLabel | string | "Funnel chart" with enUS | Accessible chart name. Pass a localized value for other interfaces. |
| conversionLabel | string | "Conversion" with enUS | Conversion badge prefix. Pass a localized value for other interfaces. |
| className | string | — | Root class name. |
FunnelStage.tone is "neutral" | "brand" | "success" | "warning" | "danger", defaulting to brand.
Events
| Event | Type | Description |
|---|---|---|
| onStageClick | (stage) => void | Called from a stage. |
Slots
| Slot | Type | Description |
|---|---|---|
| renderStage | (stage, ctx: FunnelRenderCtx) => ReactNode | Replaces label and value; context contains widthRatio, nullable conversion, and index. |
Example
<Funnel
stages={[
{ id: "in", label: "Incoming", value: 1240, tone: "brand" },
{ id: "route", label: "Routed", value: 1080, tone: "brand" },
{ id: "exec", label: "Executed", value: 860, tone: "warning" },
{ id: "done", label: "Completed", value: 720, tone: "success" },
]}
orientation="vertical"
showConversion
ariaLabel="Funnel chart"
conversionLabel="Conversion"
onStageClick={(s) => console.log(s.id)}
/>Usage notes
- Tone is a fixed semantic enum. Use
renderStagefor arbitrary colors. computeFunnelreturnsnullconversion for the first stage and never divides by zero; custom renderers must handleconversion == null.Funnelhas no client boundary, so server components and server-compatiblerenderStagefunctions remain supported. LocalizeariaLabelandconversionLabelexplicitly when needed.
Related
Table · Book3D · ProTable · PricingTable · JsonViewer · EditableTable
Playground
<Funnel
stages={[
{ id: "in", label: "Influx", value: 1240, tone: "brand" },
{ id: "route", label: "Routing", value: 1080, tone: "brand" },
{ id: "exec", label: "Execute", value: 860, tone: "warning" },
{ id: "done", label: "Complete", value: 720, tone: "success" },
]}
orientation="vertical"
showConversion={true}
ariaLabel="Funnel chart"
conversionLabel="Conversion"
onStageClick={(s) => console.log(s.id)}
/>