# Funnel chart

> A React funnel chart of conversion: each step's count, its conversion and the drop-off between steps, as aligned bars or a tapered shape.

- Type: React component (Charts & Stats)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add funnel-chart`
- Docs: https://wingo-ui.com/components/funnel-chart

## Interaction

The bars grow in one step after the other the first time the chart scrolls into view while the counts tick up, and each drop-off chip fades in after its row; when the numbers change, the bars glide to their new length. Every number is printed on the row (step, count, conversion, and between rows the drop-off with how many were lost), so there is nothing to hover. When you pass onValueChange or onStepClick the rows become buttons: click or tap one to select it (the others fade to 55%), click it again to clear. With the keyboard the rows are one tab stop: Up / Down (or Left / Right) move between them, Home / End jump to the first and last, Enter or Space select and Escape clears. In the funnel variant the segments are revealed top to bottom (left to right when horizontal); hover a step, or tap it on a phone, to highlight it and read its value, conversion and drop-off in a readout, drag a finger along the funnel to scrub, tap outside to clear. Tab to the funnel and use the arrow keys, Home / End and PageUp / PageDown to walk the steps, Enter to select and Escape to clear; a live region reads each step. A horizontal funnel stacks top to bottom in containers narrower than 480px.

## Installation

```bash
npx wingo-ui@latest add funnel-chart
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/funnel-chart` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/funnel-chart.tsx`

npm dependencies: `npm i motion d3-shape && npm i -D @types/d3-shape`

Wingo UI dependencies (installed automatically):

- [Chart kit](https://wingo-ui.com/components/chart-kit) ([Markdown](https://wingo-ui.com/components/chart-kit.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))

## Usage

```tsx
import { FunnelChart } from "@/components/ui/funnel-chart"

const pipeline = [
  { id: "lead", label: "Leads", value: 1240 },
  { id: "qualified", label: "Qualified", value: 620 },
  { id: "proposal", label: "Proposal sent", value: 248 },
  { id: "negotiation", label: "Negotiation", value: 96 },
  { id: "won", label: "Won", value: 41 },
]

export function SalesPipeline() {
  return (
    <FunnelChart
      steps={pipeline}
      locale="en-US"
      label="Sales pipeline, September 2026"
      labels={{
        conversion: (pct, { reference }) => `${pct} of ${reference.toLowerCase()}`,
        dropoff: (pct, lost) => `${pct} · ${lost} lost`,
      }}
      onValueChange={(id) => console.log(id)}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `steps` | `FunnelStep[] = { id: string; label: ReactNode; value: number; description?: ReactNode; name?: string; color?: string }[]` |  | The steps in order, from the widest to the narrowest. name is the plain-text name when label is not a string; description is a quieter second line; color (a palette slot "series-3", a tone or any CSS color) overrides the fill of that step. Negative values draw as 0. |
| `variant?` | `"bars" \| "funnel"` | `"bars"` | bars: one aligned row per step with every number printed (the readable default and the phone layout). funnel: a tapered shape of trapezoids with the labels beside it. |
| `orientation?` | `"vertical" \| "horizontal"` | `"vertical"` | Funnel variant: top to bottom, or left to right with the labels above each step. Horizontal falls back to vertical when the chart is narrower than 480px. |
| `curve?` | `"linear" \| "smooth"` | `"linear"` | Funnel variant: straight trapezoid sides, or smooth S-curves from one step's width to the next. |
| `baseline?` | `"first" \| "previous"` | `"first"` | What the conversion percent is measured against: the first step ("3.3% of leads") or the step before it (step-to-step conversion; the first step then shows none). |
| `showConversion?` | `boolean` | `true` | The conversion percent next to each value. |
| `showDropoff?` | `boolean` | `true` | The chip between steps: how much of the previous step was lost, in percent and in units. A step larger than the one before shows a gain instead. |
| `dropoffThreshold?` | `number` | `10` | Drop-offs from this percent up are printed in the danger text color; smaller ones and gains stay calm. 0 turns the danger color off (every chip muted). |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The fill of the bars and segments. Neutral is the foreground ink (black in light mode, white in dark mode). |
| `color?` | `string` | `""` | Any CSS color for the fill; replaces the tone. Empty uses the tone. |
| `stepColors?` | `boolean` | `false` | Paint each step in its own chart palette slot (series-1, series-2...) instead of one tone. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Bars: track 8 / 12 / 16px, row spacing and text size. Funnel: 200 / 280 / 360px tall. |
| `radius?` | `"none" \| "sm" \| "md" \| "full"` | `"full"` | The corners of the bars and their tracks (bars variant). |
| `value?` | `string \| null` |  | The selected step id when you control it: the other steps fade to 55%. null clears. |
| `defaultValue?` | `string \| null` | `"null"` | The step id selected at first, uncontrolled. |
| `onValueChange?` | `(id: string \| null) => void` |  | Called when a step is selected or cleared. Passing it (or onStepClick) turns the rows into buttons. |
| `onStepClick?` | `(step: FunnelStep, index: number) => void` |  | Called when a step is clicked, tapped or activated with Enter / Space, e.g. to open the deals in that stage. |
| `valueFormat?` | `ChartValueFormat = "number" \| "compact" \| "currency" \| "percent"` | `"number"` | How the step values print in the rows, the readout and the table. Percent takes whole numbers (12.4 -> "12,4 %"). |
| `currency?` | `string` | `"USD"` | ISO 4217 code for valueFormat currency, printed with the narrow symbol ("$128,450"). |
| `decimals?` | `number` | `0` | Fraction digits for values; auto by magnitude when unset (Auto in the playground): 0 for counts, 1 for percent values. |
| `formatValue?` | `(value: number, context: { series: string; kind: "label" \| "tooltip" \| "table" }) => string` |  | Replaces the built-in value formatting everywhere (series is the step id), e.g. compact dollars on the rows and full amounts in the table. |
| `locale?` | `string` | `""` | Locale for numbers and percents. Empty uses the UIProvider / LocaleProvider locale. |
| `showTooltip?` | `boolean` | `true` | Funnel variant: the readout beside a hovered or keyboard-inspected step (pinned above the chart on touch). Bars print every number and need none. |
| `animated?` | `boolean` | `true` | Bars grow and counts tick up on first view, the funnel is revealed top to bottom, and changes glide. Reduced motion always shows the final state. |
| `loading?` | `boolean` | `false` | Skeleton rows (or a skeleton funnel) in the shape of the chart. |
| `emptyState?` | `ReactNode` |  | Replaces the built-in empty state (shown when steps is empty). |
| `label?` | `string` | `""` | Accessible name of the figure (followed by the overall conversion) and caption of its data table. Empty joins the step names. |
| `description?` | `string` | `""` | A one-sentence takeaway for screen readers ("The biggest loss is at the proposal stage"). |
| `labels?` | `Partial<FunnelChartLabels>` |  | Strings: empty, loading, inspectHint, selected (appended to the funnel's live readout on the selected step), stepHeader / valueHeader / conversionHeader / dropoffHeader / gainHeader (table and readout), and the functions conversion ((pct, { baseline, reference }) => "50.0% of first"), dropoff ((pct, lost) => "50.0% drop-off, 620 lost"), gain ((pct, gained) => ...), step ((label, value, conversion, change) => the screen reader text) and figure ((label, overall) => ...). |
| `classNames?` | `Partial<Record<FunnelChartSlot, string>>` |  | Classes for list, step, header, label, description, value, conversion, track, bar, dropoff (bars), svg, segment, stepLabel, tooltip, inspect (funnel) and table. |

## Parts

### DEFAULT_FUNNEL_CHART_LABELS (constant)

The English strings: the shared chart labels plus the keyboard hint, conversion, drop-off and gain lines, each step's spoken sentence and the table headers.

## Examples

- **Funnel shape**: variant funnel, vertical and horizontal with smooth sides.
- **Step to step**: baseline previous on a checkout, with a drop-off threshold of 40%.
- **Step colors**: Five recruiting stages, each in its own palette slot.
- **Money per stage**: Pipeline value in compact dollars, brand tone.
- **Clickable steps**: onStepClick opens the deals in that stage (a sheet, bottom on phones).
- **Gains and zero steps**: Re-entries print as a gain; a step at 0 shows 0% and a full drop-off.
- **Loading and empty**: Skeleton rows and the empty state.
- **Phone, 360px**: Selectable rows with the selection held in state.

## See also

- [Chart kit](https://wingo-ui.com/components/chart-kit) ([Markdown](https://wingo-ui.com/components/chart-kit.md))
- [Bar chart](https://wingo-ui.com/components/bar-chart) ([Markdown](https://wingo-ui.com/components/bar-chart.md))
- [Steps](https://wingo-ui.com/components/steps) ([Markdown](https://wingo-ui.com/components/steps.md))
- [Progress](https://wingo-ui.com/components/progress) ([Markdown](https://wingo-ui.com/components/progress.md))

---

Canonical: https://wingo-ui.com/components/funnel-chart
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
