Sankey chart
Visitor flow
| From | To | Amount |
|---|---|---|
| Search | Pricing | 2,400 |
| Search | Product tour | 1,800 |
| Social | Product tour | 1,500 |
| Social | Pricing | 500 |
| Pricing | 900 | |
| Product tour | 300 | |
| Pricing | Signed up | 1,300 |
| Pricing | Left | 2,500 |
| Product tour | Signed up | 900 |
| Product tour | Left | 2,700 |
Usage
Import the component and the stylesheet once. See Getting started to install the package.
import { SankeyChartCard } from '@lilt-ui/charts';
import '@lilt-ui/charts/styles.css';<SankeyChartCard
title="Visitor flow"
data={visitorFlow}
source="from"
target="to"
value="visitors"
/>Omit title to leave out the visible title. Set header={false} to remove the whole header, and use aria-label to name a chart inside your own card. surface="ghost" controls only the visual frame. See Card composition.
Data
Each row is one flow: source and target name the steps, and value says how much moved; TypeScript rejects anything else. Steps come from the names themselves and fall into columns by how many steps lead to them. Flows must move forward, so a loop shows an error. A step may pass on less than it receives, as when visitors leave without a tracked exit; conservation="strict" requires every middle step to balance. A flow that is null isn’t drawn, the footer says so, and totals count measured flows only.
// One row per flow, from one step to the next.
const visitorFlow = [
{ from: 'Search', to: 'Pricing', visitors: 2400 },
{ from: 'Pricing', to: 'Signed up', visitors: 1300 },
{ from: 'Email', to: 'Product tour', visitors: null }, // not measured
// …
];
<SankeyChartCard
title="Visitor flow"
data={visitorFlow}
source="from" // a text field: the step a flow leaves
target="to" // a text field: the step it reaches
value="visitors" // a numeric field
/>For a working period select, pass ranges. Each range brings its own data and delta.
<SankeyChartCard
title="Visitor flow"
source="from"
target="to"
value="visitors"
ranges={[
{ id: 'week', label: 'This week', data: thisWeek, delta: 0.163 },
{ id: 'last-week', label: 'Last week', data: lastWeek, delta: 0.042 },
]}
/>Props
Every prop SankeyChartCard accepts, in the order you are likely to reach for them.
| Prop | Type | Description |
|---|---|---|
title | string | Optional visible title. Omit to render no title. Also supplies the accessible name unless aria-label is set. |
header | boolean | Defaults to true. False removes the whole header: title, headline, delta and period controls, with no reserved space. Pass data directly when your app owns period selection. |
aria-label | string | Accessible chart name, independent of the visible title. Falls back to title, then a generic chart name. |
data | Row[] | One row per flow. Omit when using ranges. |
source | text key of Row | Names the step a flow leaves. |
target | text key of Row | Names the step a flow reaches. |
value | numeric key of Row | How much moved. `null` means not measured: not drawn, and not counted. |
conservation | 'loss' | 'strict' | Loss (default) lets a step pass on less than it receives; strict requires every middle step to balance. |
height | number | Plot height in pixels. Defaults to 280. |
headline | number | Resting headline. Defaults to everything entering the first steps. |
delta | number | Fractional change for the chip. |
deltaTone | 'default' | 'inverse' | 'neutral' | Use inverse when a decrease is good. |
range | string | Static period label in the header. |
ranges | { id, label, data, delta?, headline? }[] | Turns the period label into a select. Each range swaps the flows. |
depth | boolean | Draws flows as lit pipes of their exact width and nodes as small blocks. |
valueFormat | Intl.NumberFormatOptions | Formats the headline, tiles, and axis, e.g. currency. |
surface | 'elevated' | 'outline' | 'ghost' | Card treatment. Ghost removes the visual frame only; header and legend are independent controls. |
palette | 'iris' | 'cobalt' | 'emerald' | Series colors. Iris by default; explicit series colors still win. |
numberStyle | 'count' | 'pop' | 'slide' | 'roll' | 'flow' | 'scramble' | How the headline animates when its value changes. Count (default) counts to the new value. |
loading | boolean | Shows a neutral loading skeleton, not data. |
loadingStyle | 'shimmer' | 'draw' | 'breathe' | Shimmer sweeps a highlight over still shapes; draw reveals them in order; breathe lets them settle. Follows the selected look, including depth, and respects reduced motion. |
empty | 'dots' | 'shape' | ReactElement | null | What the card shows when the period has no data. Dots (default) gathers a quiet dot field around the message; shape lays the chart’s own outline behind it. Pass `<ChartEmpty>` with your own words, any element of your own, or null for nothing. Both looks stay still, so empty never reads as loading, and the headline reads a dash, never zero. |
motion | 'auto' | 'none' | None turns off decorative motion; reduced motion does this automatically. |
defaultRange | string | Initial range id; defaults to the first supplied range. |
locale | string | Locale used for number formatting. Defaults to en-US. |
formatValue | (value: number) => string | Custom value text, taking precedence over valueFormat. |
badge | ReactNode | Content in the tab above the card. A comparison badge can replace this while a comparison is active. |
className | string | Additional class on the card frame. |
style | CSSProperties | ChartStyle | Inline styles and scoped --lilt-* chart variables. |