Skip to content
Data & ChartsPro

Sankey Chart

Sankey chart for flows between stages: basic and gradient flows, a vertical sankey, an alluvial and a chord diagram.

Usage

Traffic flow

Traffic from channels to signups: 15 flows between 9 nodes. Largest: Docs → Left 3.5k, Search → Docs 3.2k, Blog → Left 2.8k.
Traffic from channels to signups, data table
labelFlow
Search → Docs3.2k
Search → Blog1.8k
Search → Pricing0.9k
Direct → Pricing1.6k
Direct → Docs0.9k
Social → Blog1.4k
Social → Pricing0.3k
Referral → Docs0.7k
Referral → Pricing0.5k
Pricing → Signup1.9k
Pricing → Left1.4k
Docs → Signup1.3k
Docs → Left3.5k
Blog → Signup0.4k
Blog → Left2.8k

Installation

Sankey Chart comes with WunderUI Core and Pro, in the package from your plan. See the plans.

Then import what you need:

tsx
import {
  SankeyChart,
  SankeyChartProps,
  SankeyChartVariant,
} from "@wunderui/react"

Examples

Basic Sankey

Traffic flow

Traffic from channels to signups: 15 flows between 9 nodes. Largest: Docs → Left 3.5k, Search → Docs 3.2k, Blog → Left 2.8k.
Traffic from channels to signups, data table
labelFlow
Search → Docs3.2k
Search → Blog1.8k
Search → Pricing0.9k
Direct → Pricing1.6k
Direct → Docs0.9k
Social → Blog1.4k
Social → Pricing0.3k
Referral → Docs0.7k
Referral → Pricing0.5k
Pricing → Signup1.9k
Pricing → Left1.4k
Docs → Signup1.3k
Docs → Left3.5k
Blog → Signup0.4k
Blog → Left2.8k

Gradient Sankey

Traffic flow

Traffic from channels to signups: 15 flows between 9 nodes. Largest: Docs → Left 3.5k, Search → Docs 3.2k, Blog → Left 2.8k.
Traffic from channels to signups, data table
labelFlow
Search → Docs3.2k
Search → Blog1.8k
Search → Pricing0.9k
Direct → Pricing1.6k
Direct → Docs0.9k
Social → Blog1.4k
Social → Pricing0.3k
Referral → Docs0.7k
Referral → Pricing0.5k
Pricing → Signup1.9k
Pricing → Left1.4k
Docs → Signup1.3k
Docs → Left3.5k
Blog → Signup0.4k
Blog → Left2.8k

Vertical Sankey

Traffic flow

Traffic from channels to signups: 15 flows between 9 nodes. Largest: Docs → Left 3.5k, Search → Docs 3.2k, Blog → Left 2.8k.
Traffic from channels to signups, data table
labelFlow
Search → Docs3.2k
Search → Blog1.8k
Search → Pricing0.9k
Direct → Pricing1.6k
Direct → Docs0.9k
Social → Blog1.4k
Social → Pricing0.3k
Referral → Docs0.7k
Referral → Pricing0.5k
Pricing → Signup1.9k
Pricing → Left1.4k
Docs → Signup1.3k
Docs → Left3.5k
Blog → Signup0.4k
Blog → Left2.8k

Alluvial

Plan changes

Plan changes per quarter (accounts): 18 flows between 11 nodes. Largest: Free → Free 60, Pro → Pro 52, Free → Free 48.
Plan changes per quarter (accounts), data table
labelFlow
Free → Free60
Free → Pro18
Free → Churned22
Pro → Pro44
Pro → Enterprise8
Pro → Free4
Pro → Churned6
Enterprise → Enterprise20
Enterprise → Pro2
Free → Free48
Free → Pro10
Free → Churned6
Pro → Pro52
Pro → Enterprise6
Pro → Free4
Pro → Churned2
Enterprise → Enterprise27
Enterprise → Churned1

Chord

Travel between hubs

Travel between hubs: 20 flows between 5 nodes. Largest: Berlin → London 42k trips, London → Berlin 38k trips, Paris → London 26k trips.
Travel between hubs, data table
labelFlow
Berlin → London42k trips
Berlin → Paris18k trips
Berlin → Madrid9k trips
Berlin → Rome6k trips
London → Berlin38k trips
London → Paris22k trips
London → Madrid12k trips
London → Rome4k trips
Paris → Berlin14k trips
Paris → London26k trips
Paris → Madrid20k trips
Paris → Rome10k trips
Madrid → Berlin8k trips
Madrid → London10k trips
Madrid → Paris24k trips
Madrid → Rome16k trips
Rome → Berlin4k trips
Rome → London6k trips
Rome → Paris12k trips
Rome → Madrid18k trips

State – Loading

Traffic flow

Traffic from channels to signups: 15 flows between 9 nodes. Largest: Docs → Left 3.5k, Search → Docs 3.2k, Blog → Left 2.8k.

In context

How the component is used inside real screens — the same compositions as the Usage section of the Figma page.

Plan Moves

Where accounts move between plans, with the quarter's numbers.

Where Free accounts go

Plan moves over three quarters: 18 flows between 11 nodes. Largest: Free → Free 60 accounts, Pro → Pro 52 accounts, Free → Free 48 accounts.
Plan moves over three quarters, data table
labelFlow
Free → Free60 accounts
Free → Pro18 accounts
Free → Churned22 accounts
Pro → Pro44 accounts
Pro → Enterprise8 accounts
Pro → Free4 accounts
Pro → Churned6 accounts
Enterprise → Enterprise20 accounts
Enterprise → Pro2 accounts
Free → Free48 accounts
Free → Pro10 accounts
Free → Churned6 accounts
Pro → Pro52 accounts
Pro → Enterprise6 accounts
Pro → Free4 accounts
Pro → Churned2 accounts
Enterprise → Enterprise27 accounts
Enterprise → Churned1 accounts

This quarter

  • Upgraded to Pro
    10
  • Upgraded to Enterprise
    6
  • Churned
    37
  • Net new paying
    +12

Retention

  • Free
    75%
  • Pro
    81%
  • Enterprise
    96%

API Reference

PropTypeDefault
variant"basic-sankey" | "gradient-sankey" | "vertical-sankey" | "alluvial" | "chord""basic-sankey"
nodes{ id, label?, group?, color? }[]—
links{ source, target, value }[]—
groups · columns{ key, label?, color? }[] · string[]—
valueFormatter(v: number) => string—
state"ready" | "loading" | "empty" | "error""ready"
hiddenSeries · animate · animationKeystring[] · boolean · string | number—
activeIndexnumber | null—
animateboolean—
animationKeystring | number—
classNamestring—
columnsreadonly string[]—
defaultActiveIndexnumber | null—
descriptionstring—
groupsreadonly { key: string; label?: string; color?: ChartColor }[]—
heightnumber—
hiddenSeriesreadonly string[]—
labelstring—
onActiveIndexChange(index: number | null) => void—
onRetry() => void—
widthnumber—

Theming

The CSS variables this component reads. Set one on any wrapper to restyle everything inside it. More in Theming.

VariableUsed forDefault
Color
Fill#000000dark #FFFFFF
Fill#454648dark #D1D1D1
Fill#6E6E70dark #A2A3A3
Motion
Duration250ms
Easingcubic-bezier(0.22, 1, 0.36, 1)