# FunnelChart

Stages that only ever narrow.

- Group: Charts
- Import: `import { FunnelChart } from '@misoto22/folio/charts'`
- Page: https://ui.misoto22.com/components/funnel-chart/
- Related: sankey-chart, bar-chart

## When to reach for it

A signup flow, a hiring pipeline, a checkout. When the flow can SPLIT rather than only shrink, it is a SankeyChart — a funnel has one path through it by construction.

## Anatomy

- **Figure frame** (required) — ChartFigure’s <figure>, with an empty state at zero stages. There is still no loading skeleton, so that state remains the call site’s job.
- **Stages** (required) — <FunnelChart.Funnel>. The rows are drawn in the order given and never sorted, and each stage is cut from the next by a --chart-surface stroke gap pixels wide rather than by a transparent gap, so the stages still touch.
- **Stage labels** — <FunnelChart.Label>, positioned right by default. Its dataKey defaults to the NAME field, not the value — printing the numbers means pointing it at the value key yourself.
- **Tooltip** — <FunnelChart.Tooltip>, heading suppressed and cursor off. There is no axis here for a crosshair to run along.
- **Hidden data table** — The sr-only table, one row per stage. It is the only exact reading this form offers, because a funnel has no axis and no ticks anywhere on it.

## Best practices

### Do

- Point <FunnelChart.Label> at the value field. With no dataKey it prints the stage NAME, which the reader already has, and the number — the one thing the taper cannot be read for — goes unprinted.
- Order the rows widest first yourself. The component draws them in the order it is handed and does not sort, so a stage out of place renders a funnel that widens, which a reader will read as a data error.
- Keep variant="stepped" unless the stages are also a sequence of kinds. ramp walks the series ramp stage by stage, which encodes the drop a second time when the taper has already said it.

### Don’t

- Do not use it for a flow that splits. A funnel has one path through it by construction; where a stage divides into two outcomes the honest form is a SankeyChart, which can draw both branches.
- Do not read the fall-off off the shape. The taper is a ratio between neighbours and the eye reads the enclosed AREA, so a shallow drop is exaggerated and a steep one flattened, with no axis anywhere to check it against.

## Accessibility

- title is required; the stages are also rendered as a visually hidden table.
- The taper encodes a ratio between neighbouring stages and the eye reads the enclosed area, so a funnel exaggerates a shallow drop. Compose a Label to print the numbers — that is the relief.
- The default variant holds one fill for every stage and lets the shape carry the drop. A ramp that also darkens each stage encodes the same fact twice.

## FunnelChart

Stages that only ever narrow — a signup flow, a hiring pipeline, a checkout. The caveat is the same one every funnel has: the taper encodes a RATIO between neighbouring stages, and the eye reads the enclosed area, so a funnel exaggerates a shallow drop and flattens a steep one. Where the exact fall-off is the point, put the percentages on the stages — `<Funnel.Label>` does — or use a `<BarChart>`, which encodes each stage on one honest scale. Reach for `<SankeyChart>` instead when the flow can SPLIT rather than only shrink: a funnel has one path through it by construction.

### Props

- `config` (required) — `ChartConfig`. Stage names → their label and paint. Keys must match the `nameKey` values.
- `data` (required) — `TData[]`. The stages, widest first. Order is the funnel; it is not sorted for you.
- `dataKey` (required) — `keyof TData & string`. The row field holding each stage's number.
- `nameKey` (required) — `keyof TData & string`. The row field naming each stage.
- `title` (required) — `string`. What the chart shows, in a sentence a reader could act on. Required, and announced to a screen reader even when it is not printed.
- `showTitle` — `boolean`. Prints the title above the plot instead of hiding it from sight.
- `description` — `ReactNode`. A line under the title — the unit, the window, the caveat.
- `children` (required) — `ReactNode`. The composed parts — `<Funnel>`, `<Tooltip>`.
- `className` — `string`. Merged onto the figure, last, so a call site can size or space it.
- `chartProps` — `ComponentProps<typeof RechartsFunnelChart>`. Escape hatch onto the raw Recharts chart element.
- `hideDataTable` — `boolean` default `false`. Drops the hidden table view. Only correct when the page prints the data itself.
- `empty` — `ChartEmptyProps | false`. What the chart shows when it has nothing to draw. `false` keeps the empty plot, for a chart whose emptiness is itself the reading.

## Example — default

```tsx
import { FunnelChart, type ChartConfig } from '@misoto22/folio/charts'

<FunnelChart
  title="Signup funnel"
  config={config}
  data={stages}
  dataKey="people"
  nameKey="stage"
>
  <FunnelChart.Funnel>
    <FunnelChart.Label />
  </FunnelChart.Funnel>
  <FunnelChart.Tooltip />
</FunnelChart>
```

## Example — variant

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { FunnelChart, type ChartConfig, type FunnelVariant } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={variant}
    onValueChange={(next) => next && setVariant(next as FunnelVariant)}
    aria-label="Stage fill"
  >
    {VARIANTS.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <FunnelChart
    title={`Signup funnel — ${variant}`}
    config={config}
    data={stages}
    dataKey="people"
    nameKey="stage"
  >
    <FunnelChart.Funnel variant={variant} gap={3}>
      <FunnelChart.Label dataKey="people" position="center" />
    </FunnelChart.Funnel>
    <FunnelChart.Tooltip />
  </FunnelChart>
</div>
```

## Example — stage to stage rates

```tsx
import { FunnelChart, type ChartConfig } from '@misoto22/folio/charts'

<FunnelChart
  title="Signup funnel, stage to stage"
  showTitle
  description="Each stage as a share of the one above it"
  config={config}
  data={stages}
  dataKey="people"
  nameKey="stage"
>
  <FunnelChart.Funnel>
    <FunnelChart.Label position="center" />
    <FunnelChart.Label dataKey="kept" position="right" />
  </FunnelChart.Funnel>
  <FunnelChart.Tooltip />
</FunnelChart>
```
