Sankey chart
Shows how a quantity flows from sources through stages to destinations, with link width proportional to the flow.
- Use when
- Where things come from and where they go: energy from fuel to end use, budget from revenue to spending, users from channel to outcome. Works best with 2 to 4 stages and up to about 20 nodes.
- Avoid when
- A single sequence of steps with drop-off (use funnel chart); comparing a few totals (use bar chart); flows that loop back (cycles cannot be drawn and are dropped); more than about 40 links.
Preview
220 TWh of the 860 TWh used to generate electricity was lost
Energy flows from fuel to end use, terawatt-hours per year
import { SankeyChart } from "insightcharts";
const data = [
{ from: "Coal", to: "Electricity", twh: 290 },
{ from: "Gas", to: "Electricity", twh: 210 },
{ from: "Gas", to: "Heat", twh: 330 },
{ from: "Wind and solar", to: "Electricity", twh: 240 },
{ from: "Nuclear", to: "Electricity", twh: 120 },
{ from: "Oil", to: "Transport", twh: 560 },
{ from: "Oil", to: "Heat", twh: 90 },
{ from: "Electricity", to: "Homes", twh: 280 },
{ from: "Electricity", to: "Industry", twh: 330 },
{ from: "Electricity", to: "Transport", twh: 30 },
{ from: "Electricity", to: "Losses", twh: 220 },
{ from: "Heat", to: "Homes", twh: 270 },
{ from: "Heat", to: "Industry", twh: 150 },
];
export function Example() {
return (
<SankeyChart
data={data}
from="from"
to="to"
value="twh"
format={"{value} TWh"}
title="220 TWh of the 860 TWh used to generate electricity was lost"
subtitle="Energy flows from fuel to end use, terawatt-hours per year"
source="Illustrative national energy balance"
/>
);
}{
"type": "sankey-chart",
"data": [
{ "from": "Coal", "to": "Electricity", "twh": 290 },
{ "from": "Gas", "to": "Electricity", "twh": 210 },
{ "from": "Gas", "to": "Heat", "twh": 330 },
{ "from": "Wind and solar", "to": "Electricity", "twh": 240 },
{ "from": "Nuclear", "to": "Electricity", "twh": 120 },
{ "from": "Oil", "to": "Transport", "twh": 560 },
{ "from": "Oil", "to": "Heat", "twh": 90 },
{ "from": "Electricity", "to": "Homes", "twh": 280 },
{ "from": "Electricity", "to": "Industry", "twh": 330 },
{ "from": "Electricity", "to": "Transport", "twh": 30 },
{ "from": "Electricity", "to": "Losses", "twh": 220 },
{ "from": "Heat", "to": "Homes", "twh": 270 },
{ "from": "Heat", "to": "Industry", "twh": 150 }
],
"from": "from",
"to": "to",
"value": "twh",
"format": "{value} TWh",
"title": "220 TWh of the 860 TWh used to generate electricity was lost",
"subtitle": "Energy flows from fuel to end use, terawatt-hours per year",
"source": "Illustrative national energy balance"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Used in
No molecule or template lists sankey chart in its composes. It still fits any slot that takes a component.
Installation
$ pnpm dlx shadcn@latest add @insightcharts/sankey-chartThe CLI copies the source into your project and needs the @insightcharts registry in components.json; see installation. With the package, import it by name:
import { SankeyChart } from "insightcharts";Examples
Two stages, highlight
52% of referred visitors signed up last quarter
Visitors by acquisition channel and outcome, last quarter
import { SankeyChart } from "insightcharts";
const data = [
{ channel: "Organic search", outcome: "Signed up", users: 4200 },
{ channel: "Organic search", outcome: "Left", users: 9800 },
{ channel: "Paid ads", outcome: "Signed up", users: 1500 },
{ channel: "Paid ads", outcome: "Left", users: 10500 },
{ channel: "Referral", outcome: "Signed up", users: 2600 },
{ channel: "Referral", outcome: "Left", users: 2400 },
];
export function Example() {
return (
<SankeyChart
data={data}
from="channel"
to="outcome"
value="users"
highlight="Referral"
height={300}
title="52% of referred visitors signed up last quarter"
subtitle="Visitors by acquisition channel and outcome, last quarter"
note="Illustrative data"
/>
);
}{
"type": "sankey-chart",
"data": [
{ "channel": "Organic search", "outcome": "Signed up", "users": 4200 },
{ "channel": "Organic search", "outcome": "Left", "users": 9800 },
{ "channel": "Paid ads", "outcome": "Signed up", "users": 1500 },
{ "channel": "Paid ads", "outcome": "Left", "users": 10500 },
{ "channel": "Referral", "outcome": "Signed up", "users": 2600 },
{ "channel": "Referral", "outcome": "Left", "users": 2400 }
],
"from": "channel",
"to": "outcome",
"value": "users",
"highlight": "Referral",
"height": 300,
"title": "52% of referred visitors signed up last quarter",
"subtitle": "Visitors by acquisition channel and outcome, last quarter",
"note": "Illustrative data"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Vertical
Borrowing funded £130bn of the £1,040bn budget
Government revenue by source and spending by area, £ billion
import { SankeyChart } from "insightcharts";
const data = [
{ from: "Taxes", to: "Budget", bn: 820 },
{ from: "Borrowing", to: "Budget", bn: 130 },
{ from: "Other", to: "Budget", bn: 90 },
{ from: "Budget", to: "Health", bn: 250 },
{ from: "Budget", to: "Welfare", bn: 310 },
{ from: "Budget", to: "Schools", bn: 120 },
{ from: "Budget", to: "Defence", bn: 60 },
{ from: "Budget", to: "Interest", bn: 110 },
{ from: "Budget", to: "Rest", bn: 190 },
];
export function Example() {
return (
<SankeyChart
data={data}
from="from"
to="to"
value="bn"
format={"£{value}bn"}
orientation="vertical"
title="Borrowing funded £130bn of the £1,040bn budget"
subtitle="Government revenue by source and spending by area, £ billion"
note="Illustrative data"
/>
);
}{
"type": "sankey-chart",
"data": [
{ "from": "Taxes", "to": "Budget", "bn": 820 },
{ "from": "Borrowing", "to": "Budget", "bn": 130 },
{ "from": "Other", "to": "Budget", "bn": 90 },
{ "from": "Budget", "to": "Health", "bn": 250 },
{ "from": "Budget", "to": "Welfare", "bn": 310 },
{ "from": "Budget", "to": "Schools", "bn": 120 },
{ "from": "Budget", "to": "Defence", "bn": 60 },
{ "from": "Budget", "to": "Interest", "bn": 110 },
{ "from": "Budget", "to": "Rest", "bn": 190 }
],
"from": "from",
"to": "to",
"value": "bn",
"format": "£{value}bn",
"orientation": "vertical",
"title": "Borrowing funded £130bn of the £1,040bn budget",
"subtitle": "Government revenue by source and spending by area, £ billion",
"note": "Illustrative data"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Stress: four stages, many nodes, long names, a loop
Power stations lost 400 PJ of the 1,060 PJ they took in
National energy balance from primary supply to final use, petajoules
import { SankeyChart } from "insightcharts";
const data = [
{ from: "Domestic crude oil production", to: "Refineries", pj: 410 },
{ from: "Imported crude oil", to: "Refineries", pj: 620 },
{ from: "Natural gas fields", to: "Gas processing plants", pj: 540 },
{ from: "Imported liquefied natural gas", to: "Gas processing plants", pj: 180 },
{ from: "Coal mines", to: "Power stations", pj: 260 },
{ from: "Wind farms", to: "Power stations", pj: 150 },
{ from: "Solar parks", to: "Power stations", pj: 90 },
{ from: "Hydroelectric dams", to: "Power stations", pj: 70 },
{ from: "Nuclear reactors", to: "Power stations", pj: 210 },
{ from: "Refineries", to: "Road transport", pj: 610 },
{ from: "Refineries", to: "Aviation and shipping", pj: 240 },
{ from: "Refineries", to: "Petrochemicals", pj: 180 },
{ from: "Gas processing plants", to: "Power stations", pj: 280 },
{ from: "Gas processing plants", to: "Residential heating", pj: 290 },
{ from: "Gas processing plants", to: "Heavy industry", pj: 150 },
{ from: "Power stations", to: "Residential heating", pj: 180 },
{ from: "Power stations", to: "Heavy industry", pj: 260 },
{ from: "Power stations", to: "Commercial buildings", pj: 220 },
{ from: "Power stations", to: "Conversion and transmission losses", pj: 400 },
{ from: "Heavy industry", to: "Exported manufactured goods", pj: 190 },
{ from: "Heavy industry", to: "Domestic consumption", pj: 220 },
{ from: "Petrochemicals", to: "Exported manufactured goods", pj: 80 },
{ from: "Petrochemicals", to: "Domestic consumption", pj: 100 },
{ from: "Domestic consumption", to: "Refineries", pj: 5 },
];
export function Example() {
return (
<SankeyChart
data={data}
from="from"
to="to"
value="pj"
format={"{value} PJ"}
height={520}
title="Power stations lost 400 PJ of the 1,060 PJ they took in"
subtitle="National energy balance from primary supply to final use, petajoules"
note="Illustrative data. One circular flow in the data is left out"
/>
);
}{
"type": "sankey-chart",
"data": [
{ "from": "Domestic crude oil production", "to": "Refineries", "pj": 410 },
{ "from": "Imported crude oil", "to": "Refineries", "pj": 620 },
{ "from": "Natural gas fields", "to": "Gas processing plants", "pj": 540 },
{
"from": "Imported liquefied natural gas",
"to": "Gas processing plants",
"pj": 180
},
{ "from": "Coal mines", "to": "Power stations", "pj": 260 },
{ "from": "Wind farms", "to": "Power stations", "pj": 150 },
{ "from": "Solar parks", "to": "Power stations", "pj": 90 },
{ "from": "Hydroelectric dams", "to": "Power stations", "pj": 70 },
{ "from": "Nuclear reactors", "to": "Power stations", "pj": 210 },
{ "from": "Refineries", "to": "Road transport", "pj": 610 },
{ "from": "Refineries", "to": "Aviation and shipping", "pj": 240 },
{ "from": "Refineries", "to": "Petrochemicals", "pj": 180 },
{ "from": "Gas processing plants", "to": "Power stations", "pj": 280 },
{ "from": "Gas processing plants", "to": "Residential heating", "pj": 290 },
{ "from": "Gas processing plants", "to": "Heavy industry", "pj": 150 },
{ "from": "Power stations", "to": "Residential heating", "pj": 180 },
{ "from": "Power stations", "to": "Heavy industry", "pj": 260 },
{ "from": "Power stations", "to": "Commercial buildings", "pj": 220 },
{ "from": "Power stations", "to": "Conversion and transmission losses", "pj": 400 },
{ "from": "Heavy industry", "to": "Exported manufactured goods", "pj": 190 },
{ "from": "Heavy industry", "to": "Domestic consumption", "pj": 220 },
{ "from": "Petrochemicals", "to": "Exported manufactured goods", "pj": 80 },
{ "from": "Petrochemicals", "to": "Domestic consumption", "pj": 100 },
{ "from": "Domestic consumption", "to": "Refineries", "pj": 5 }
],
"from": "from",
"to": "to",
"value": "pj",
"format": "{value} PJ",
"height": 520,
"title": "Power stations lost 400 PJ of the 1,060 PJ they took in",
"subtitle": "National energy balance from primary supply to final use, petajoules",
"note": "Illustrative data. One circular flow in the data is left out"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| from | string | "source" | Field naming where each flow starts. Not called source because that is the figure's source line. |
| to | string | "target" | Field naming where each flow ends. |
| value | string | "value" | Flow size field. Non-positive flows are dropped. |
| orientation | "auto" | "horizontal" | "vertical" | "auto" | Left to right, or top to bottom for narrow columns with short names. |
| labels | boolean | true | Print each node's total after its name. |
| names | Record<string, string> | Display names for node keys. |
Common chart props 29
Every chart built with defineChart accepts these on top of its own props.
| Prop | Type | Description |
|---|---|---|
| data | DataInput | Rows, CSV text, a URL, { url, path }, an async function, or an Arrow-style table. Omit inside <InsightData>. |
| transform | Transform | Transform[] | A pipeline of transforms applied before drawing. |
| title | string | The title. One plain statement of what the data shows, with the number and the period ("Monthly churn fell from 4.2% to 2.1% in 2024"), or a plain label ("Monthly churn, 2024"). One clause, no figures of speech. |
| subtitle | string | Measure, unit, population, period. |
| source | string | Data source, shown as "Source: ...". |
| note | string | Method notes or caveats. |
| height | number | string | CSS height of the plot. Default 360. |
| aspect | number | Width / height. Overrides height. |
| highlight | string | string[] | The entity (or entities) the title is about. Every other series is drawn in grey. |
| annotations | Annotation[] | Reference lines, shaded bands and callouts, in data coordinates. |
| format | FormatSpec | How to print values: "compact", "percent", "currency:USD", "{value} kg", Intl options, or a function. |
| legend | boolean | Show the legend. Default: shown when there are two or more series. |
| select | boolean | string | Make marks clickable to filter. Inside <InsightData> the selection filters every other chart. Pass a field name, or true to use the chart's category field. |
| onSelect | (values: string[], field: string) => void | Called with the selected values whenever the selection changes. |
| option | EChartsOption | ((option: EChartsOption, ctx: BuildContext) => EChartsOption) | Deep-merged over the generated ECharts option, or a function that receives and returns it. Use it for ECharts settings no prop covers. |
| theme | ThemeInput | A theme, or a partial set of tokens merged over the surrounding theme, for this chart only. |
| mode | ModeInput | "auto" (default) follows the page, "system" follows the OS, or force "light" / "dark". |
| renderer | RendererInput | "auto" (default) uses SVG for light charts and canvas for heavy ones. |
| animate | boolean | Set false to disable animation. Reduced-motion preferences are always respected. |
| lazy | boolean | Mount the chart only when it scrolls near the viewport. Default true. |
| group | string | Link tooltips with other charts sharing this group id. |
| exportable | boolean | ExportFormat[] | Show an export menu: true or a list of formats from png, jpeg, svg, pdf, csv, json. |
| table | boolean | Show a collapsible "View data" table under the chart. |
| alt | string | Accessible description: the chart type and measures, then the main pattern and the notable values. |
| id | string | Stable id for this chart's selection in the shared store. |
| onEvents | Record<string, ChartEventHandler> | ECharts event handlers, e.g. { click: (params, chart) => {} }. Prefix a name with zr: to bind to the canvas itself. |
| onReady | (chart: EChartsInstance) => void | Called with the ECharts instance once it is created. |
| className | string | Class name for the outer <figure>. |
| style | CSSProperties | Inline style for the outer <figure>. |