Sankey Chart

A generic flow diagram — nodes connected by value-proportional links — for visualising money (or any proportional) flows between stages.

"use client";import { SankeyChart, type SankeyLinkDatum, type SankeyNodeDatum } from "@reva/ui";const nodes: SankeyNodeDatum[] = [{ id: "salary", label: "Salary", color: "var(--chart-1)" },{ id: "dividend", label: "Dividend", color: "var(--chart-2)" },{ id: "household", label: "Household", color: "var(--chart-accent-fill)" },{ id: "spend", label: "Living costs", color: "var(--chart-3)" },{ id: "tax", label: "Tax", color: "var(--chart-negative-fill)" },{ id: "savings", label: "Savings", color: "var(--chart-positive-fill)" },];const links: SankeyLinkDatum[] = [{ source: "salary", target: "household", value: 85_000 },{ source: "dividend", target: "household", value: 40_000 },{ source: "household", target: "spend", value: 62_000 },{ source: "household", target: "tax", value: 28_000 },{ source: "household", target: "savings", value: 35_000 },];export function HouseholdCashflow() {return (  <SankeyChart    nodes={nodes}    links={links}    label="Household cashflow: income sources into spend, tax and savings"  />);}

Usage

import { SankeyChart, type SankeyLinkDatum, type SankeyNodeDatum } from "@reva/ui";

<SankeyChart nodes={nodes} links={links} />

SankeyChart renders nodes and value-proportional links built on recharts' native Sankey chart. Nodes and links are addressed by a stable id rather than recharts' index-based shape, so callers never hand-manage array positions — a link is just { source: "salary", target: "household", value: 85_000 }.

Node placement is inferred from the graph itself: a node with no incoming links is a source (label to its right), one with no outgoing links is a sink (label to its left), and anything in between is a waypoint (label above). This makes the component shape-agnostic — a single source fanning out to several sinks, a funnel of many sources into one node, or a full three-tier flow all lay out sensibly with no extra configuration.

Stable references

Pass nodes / links with stable references — module scope, state, or useMemo.

Customisation

Omit a node's color to fall back to the categorical --chart-1--chart-7 ramp by index; pass chart role tokens (--chart-positive-fill, --chart-negative-fill, --chart-accent-fill, …) when a node carries meaning — a tax sink, a savings sink. A link's colour defaults to its source node's colour and renders as a gradient into its target's, so a link visually hands off from one node's colour to the next.

valueFormatter shapes the tooltip values, defaulting to compact en-GB numbers. Pass a compact-GBP formatter for money flows:

const gbp = new Intl.NumberFormat("en-GB", {
  style: "currency",
  currency: "GBP",
  notation: "compact",
  maximumFractionDigits: 1,
});

<SankeyChart nodes={nodes} links={links} valueFormatter={(value) => gbp.format(value)} />

Examples

A single source, several sinks

The layout isn't tied to a three-tier "sources → hub → sinks" shape — a lone source fanning straight out to multiple sinks lays out just as well.

const nodes: SankeyNodeDatum[] = [  { id: "company", label: "Acme Ltd", color: "var(--chart-accent-fill)" },  { id: "salary", label: "Salary", color: "var(--chart-1)" },  { id: "dividend", label: "Dividend", color: "var(--chart-2)" },  { id: "pension", label: "Pension", color: "var(--chart-3)" },];const links: SankeyLinkDatum[] = [  { source: "company", target: "salary", value: 40_000 },  { source: "company", target: "dividend", value: 25_000 },  { source: "company", target: "pension", value: 10_000 },];<SankeyChart  nodes={nodes}  links={links}  height={200}  label="Company extraction split by salary, dividend and pension"/>

Props

PropTypeDefaultDescription
nodesSankeyNodeDatum[]{ id, label, color? } — required
linksSankeyLinkDatum[]{ source, target, value, color? }, source/target are node ids — required
heightnumber280Chart height in px; width fills the container
valueFormatter(value: number) => stringcompact en-GB numberFormats tooltip values
labelstringAccessible label for the chart region
classNamestringApplied to the chart container

On this page