sankey
A Sankey diagram is a specific type of flow diagram used to describe the flow from one set of values to another. Sankey diagrams have the following characteristics:
- The starting flow and ending flow are the same, with the sum of all main branch widths equal to the sum of all split branch widths, maintaining energy balance;
- Internally, different lines represent different flow distribution scenarios, with widths proportionally displaying the flow share of each branch;
- Different node widths represent flow magnitudes under specific states. Sankey diagrams are commonly used for visualization analysis of energy, material composition, financial, and other data.
Getting Started
import { Chart } from '@antv/g2';
const chart = new Chart({
container: 'container',
padding: 10,
});
chart.options({
type: 'sankey',
data: {
type: 'fetch',
value: 'https://assets.antv.antgroup.com/g2/energy.json',
transform: [
{
type: 'custom',
callback: (data) => ({ links: data }),
},
],
},
layout: {
nodeAlign: 'center',
nodePadding: 0.03,
},
scale: { color: { range: schemeTableau10 } },
style: {
labelSpacing: 3,
labelFontWeight: 'bold',
nodeStrokeWidth: 1.2,
linkFillOpacity: 0.4,
},
});
chart.render();
In G2, Layout is used to specify parameters for layout methods of marks with specific layout functions, such as Sankey, WordCloud, ForceGraph, etc.
({
type: 'sankey',
layout: {
nodeAlign: 'center',
nodePadding: 0.03,
},
});
// API
chart.options({
type: 'sankey',
layout: { nodeAlign: 'center', nodePadding: 0.03 },
});
Data Update
Sankey diagrams support dynamic data updates using G2’s built-in API changeData():
const newData = {
links: [
{ source: 'A', target: 'B', value: 10 },
{ source: 'A', target: 'C', value: 15 },
{ source: 'B', target: 'D', value: 8 },
{ source: 'C', target: 'D', value: 12 },
],
};
chart.changeData({ type: 'inline', value: newData });
Syntactic Sugar (Recommended)
G2 also provides convenient syntactic sugar, allowing you to pass array data directly like configuring other charts:
const newData = [
{ source: 'A', target: 'X', value: 10 },
{ source: 'A', target: 'Y', value: 15 },
{ source: 'B', target: 'X', value: 20 },
{ source: 'B', target: 'Y', value: 25 },
];
// Pass array directly
chart.changeData(newData);
Empty Data Handling
When passing an empty array or not providing links, the chart will display as blank:
// Clear chart - chart will display as blank
chart.changeData([]);
// or
chart.changeData({ links: [] });
Sankey Diagram Data Update Example
import { Chart } from '@antv/g2';
const chart = new Chart({ container: 'container' });
const initialData = [
{ source: 'A', target: 'X', value: 10 },
{ source: 'A', target: 'Y', value: 15 },
{ source: 'B', target: 'X', value: 20 },
];
chart.options({
type: 'sankey',
data: {
type: 'inline',
value: initialData,
},
});
chart.render();
// Click event: randomly update data
chart.on('element:click', () => {
const randomData = initialData.map((d) => ({
...d,
value: Math.random() * 30 + 5,
}));
// Use simplified syntax to update data
chart.changeData(randomData);
});
Options
| Property | Description | Type | Default |
|---|---|---|---|
| tooltip | Configure Sankey diagram tooltip, see tooltip configuration | object | See tooltip configuration |
| layout | Configure Sankey diagram layout, see layout configuration | object | See layout configuration |
| style | Configure graphic and label styles, see style configuration | object | See style configuration |
| nodeLabels | Custom node data label configuration, see nodeLabels configuration | label[] | [] |
| linkLabels | Custom link data label configuration, see linkLabels configuration | label[] | [] |
tooltip
As a composite graphic, Sankey diagrams need to distinguish between nodes and links when configuring tooltip.
title
Unlike single mark title configuration, Sankey diagrams need to configure nodeTitle and linkTitle separately.
({
tooltip: {
nodeTitle: (d) => d.key,
linkTitle: (d) => 'link',
},
});
items
Unlike single mark items configuration, Sankey diagrams need to configure both nodeItems and linkItems.
Like regular items, nodeItems and linkItems also support custom configuration:
({
tooltip: {
nodeItems: [
(d, index, data, column) => {
return {
color: 'red', // Specify item color
name: 'Node', // Specify item name
value: d.key, // Use y channel value
content: 'Custom node property',
};
},
],
linkItems: [
(d, index, data, column) => {
return {
color: 'red', // Specify item color
name: 'Link', // Specify item name
value: `${d.source.key}-${d.target.key}`, // Use y channel value
content: 'Custom link property',
};
},
],
},
});
💡 How to use supplementary properties in data to customize tooltip display in Sankey diagrams?
Similar to general Mark custom tooltip interaction methods, first pass custom properties in the graphic’s tooltip, then use them in interaction.
Example:
import { Chart } from '@antv/g2';
const chart = new Chart({
container: 'container',
});
const data = {
nodes: [
{ id: 'a', key: 'Homepage', des: 'Custom node property' },
{ id: 'b', key: 'Page 1', des: 'Custom node property' },
{ id: 'b_1', key: 'Page 1', des: 'Custom node property' },
{ id: 'c', key: 'Page 2', des: 'Custom node property' },
{ id: 'c_1', key: 'Page 2', des: 'Custom node property' },
{ id: 'd', key: 'Page 3', des: 'Custom node property' },
{ id: 'd_1', key: 'Page 3', des: 'Custom node property' },
],
links: [
{ source: 'a', target: 'b', value: 100 },
{ source: 'b', target: 'c', value: 80 },
{ source: 'b', target: 'd', value: 20 },
{ source: 'c', target: 'b_1', value: 80 },
{ source: 'b_1', target: 'c_1', value: 40 },
{ source: 'b_1', target: 'd_1', value: 40 },
],
};
chart.options({
type: 'sankey',
width: 900,
height: 600,
data: {
value: data,
transform: [
{
type: 'custom',
callback: (data) => ({
nodes: data.nodes,
links: data.links,
}),
},
],
},
tooltip: {
nodeItems: [
(d, index, data, column) => {
return {
content: d.des,
};
},
],
linkItems: [
(d, index, data, column) => {
return {
color: 'red', // Specify item color
name: 'Link', // Specify item name
value: `${d.source.key}-${d.target.key}`, // Use y channel value
content: 'Custom link property',
};
},
],
},
layout: {
nodeId: (d) => d.id,
nodeAlign: 'center',
nodePadding: 0.03,
iterations: 25,
},
style: {
labelSpacing: 3,
labelFontWeight: 'bold',
// linkFillOpacity: 0.2,
// linkFill: '#3F96FF',
},
interaction: {
tooltip: {
render: (e, { items, title }) => {
return `<div>${items[0].content}</div>`;
},
},
},
});
chart.render();
layout
Sankey diagram layout configuration. Specific configuration options are as follows:
nodeId
<description>optional function </description>
Callback format: (node: any) => string. If nodeId is not specified, defaults to (node) => node.key.
Node binding field, used as unique identifier in layout.
💡 Sankey diagrams don’t support cycles, so how should we configure for scenarios like page flow diagrams where duplicate nodes appear?
For nodes that appear multiple times, set an id as unique identifier and configure the nodeId callback method as (node) => node.id.
Example:
import { Chart } from '@antv/g2';
const chart = new Chart({
container: 'container',
});
const data = {
nodes: [
{ id: 'a', key: 'Homepage' },
{ id: 'b', key: 'Page 1' },
{ id: 'b_1', key: 'Page 1' },
{ id: 'c', key: 'Page 2' },
{ id: 'c_1', key: 'Page 2' },
{ id: 'd', key: 'Page 3' },
{ id: 'd_1', key: 'Page 3' },
],
links: [
{ source: 'a', target: 'b', value: 100 },
{ source: 'b', target: 'c', value: 80 },
{ source: 'b', target: 'd', value: 20 },
{ source: 'c', target: 'b_1', value: 80 },
{ source: 'b_1', target: 'c_1', value: 40 },
{ source: 'b_1', target: 'd_1', value: 40 },
],
};
chart.options({
type: 'sankey',
width: 900,
height: 600,
data: {
value: data,
transform: [
{
type: 'custom',
callback: (data) => ({
nodes: data.nodes,
links: data.links,
}),
},
],
},
layout: {
nodeId: (d) => d.id,
nodeAlign: 'center',
nodePadding: 0.03,
iterations: 25,
},
style: {
labelSpacing: 3,
labelFontWeight: 'bold',
linkFillOpacity: 0.2,
linkFill: '#3F96FF',
},
});
chart.render();
nodeSort
<description>optional function </description>
Callback format: ((a: any, b: any) => number)
Node sorting method. If nodeSort is not specified, returns the current node sorting method, defaulting to undefined, meaning the vertical order of nodes within each column will be automatically determined by the layout. If nodeSort is null, the order is fixed by input. Otherwise, the order is determined by the specified sorting function; this function is passed two nodes and must return a value less than 0 if the first node should be above the second node, greater than 0 if the second node should be above the first node, or 0 if no order is specified.
linkSort
<description> optional function </description>
Callback format: ((a: any, b: any) => number)
Link sorting method. If linkSort is not specified, returns the current link sorting method, defaulting to undefined, meaning the vertical order of links within each node will be automatically determined by the layout. If linkSort is null, the order is fixed by input. Otherwise, the order is determined by the specified sorting function; this function is passed two links and must return a value less than 0 if the first link should be above the second link, greater than 0 if the second link should be above the first link, or 0 if no order is specified.
nodeAlign
<description>optional string | function </description>
Built-in supported types: 'left' | 'right' | 'center' | 'justify'
Callback format: ((node: any, n: number) => number
Current node alignment method. Besides the built-in types, you can also pass the current node and the graph’s total depth n (maximum node depth + 1), and must return an integer between 0 and n - 1, indicating the desired horizontal position of the node in the generated graph.
nodeWidth
<description>optional number </description>
Node width. Defaults to 0.02.
nodePadding
<description>optional number </description>
Node spacing. Defaults to 0.02.
nodeDepth
<description>optional function </description>
Callback format: (datum: any, maxDepth: number) => number
Node depth.
iterations
<description>optional number </description>
Layout calculation iteration count, defaults to 6. More iterations result in more reasonable layout.
For more layout configuration, see d3-sankey
style
Default style configuration:
({
// label
labelText: (d) => d.key,
labelSpacing: 5,
labelFontSize: 10,
// node
nodeStroke: '#000',
// link
linkFillOpacity: 0.5,
linkStroke: undefined,
});
Composite graphic marks need to use different prefixes to distinguish graphic configurations.
<label>: Prefix for configuring data labels.
| Property | Type | Description |
|---|---|---|
| labelText | (d: any) => string | Configure default data label value for Sankey diagram, defaults to (d) => d.key |
| labelSpacing | number | Configure data label spacing for Sankey diagram, defaults to 5 |
| labelFontSize | number | Sankey diagram data label font size |
| labelFontFamily | string | Sankey diagram data label font family |
| labelFontWeight | number | Sankey diagram data label font weight |
| labelLineHeight | number | Sankey diagram data label text line height |
| labelTextAlign | string | Set current alignment for Sankey diagram data label text content, supported values: center | end | left | right | start, defaults to start |
| labelTextBaseline | string | Set current text baseline used when drawing Sankey diagram data label text, supported values: top | middle | bottom | alphabetic | hanging. Defaults to bottom |
| labelFill | string | Sankey diagram data label text fill color |
| labelFillOpacity | number | Sankey diagram data label text fill opacity |
| labelStroke | string | Sankey diagram data label text stroke |
| labelLineWidth | number | Sankey diagram data label text stroke width |
| labelLineDash | [number,number] | Sankey diagram data label stroke dash configuration, first value is dash segment length, second value is gap distance. Setting lineDash to [0,0] results in no stroke. |
| labelStrokeOpacity | number | Sankey diagram data label stroke opacity |
| labelOpacity | number | Sankey diagram data label text overall opacity |
| labelShadowColor | string | Sankey diagram data label text shadow color |
| labelShadowBlur | number | Sankey diagram data label text shadow Gaussian blur coefficient |
| labelShadowOffsetX | number | Set horizontal distance of Sankey diagram data label shadow from text |
| labelShadowOffsetY | number | Set vertical distance of Sankey diagram data label shadow from text |
| labelCursor | string | Sankey diagram data label mouse cursor style. Same as CSS cursor style, defaults to ‘default’. |
<node>: Prefix for configuring nodes.
| Property | Type | Description |
|---|---|---|
| nodeFill | string | Sankey diagram node fill color |
| nodeFillOpacity | number | Sankey diagram node fill opacity |
| nodeStroke | string | Sankey diagram node stroke |
| nodeStrokeOpacity | number | Sankey diagram node stroke opacity |
| nodeLineWidth | number | Sankey diagram node stroke width |
| nodeLineDash | [number,number] | Sankey diagram node stroke dash configuration, first value is dash segment length, second value is gap distance. Setting lineDash to [0,0] results in no stroke. |
| nodeOpacity | number | Sankey diagram node overall opacity |
| nodeShadowColor | string | Sankey diagram node shadow color |
| nodeShadowBlur | number | Sankey diagram node shadow Gaussian blur coefficient |
| nodeShadowOffsetX | number | Set horizontal distance of shadow from Sankey diagram node |
| nodeShadowOffsetY | number | Set vertical distance of shadow from Sankey diagram node |
| nodeCursor | string | Sankey diagram node mouse cursor style. Same as CSS cursor style, defaults to ‘default’. |
<link>: Prefix for configuring links.
| Property | Type | Description |
|---|---|---|
| linkFill | string | Sankey diagram link fill color |
| linkFillOpacity | number | Sankey diagram link fill opacity |
| linkStroke | string | Sankey diagram link stroke |
| linkStrokeOpacity | number | Sankey diagram link stroke opacity |
| linkLineWidth | number | Sankey diagram link stroke width |
| linkLineDash | [number,number] | Sankey diagram link stroke dash configuration, first value is dash segment length, second value is gap distance. Setting lineDash to [0,0] results in no stroke. |
| linkOpacity | number | Sankey diagram link overall opacity |
| linkShadowColor | string | Sankey diagram link shadow color |
| linkShadowBlur | number | Sankey diagram link shadow Gaussian blur coefficient |
| linkShadowOffsetX | number | Set horizontal distance of shadow from Sankey diagram link |
| linkShadowOffsetY | number | Set vertical distance of shadow from Sankey diagram link |
| linkCursor | string | Sankey diagram link mouse cursor style. Same as CSS cursor style, defaults to ‘default’. |
nodeLabels
<description>optional Label[] </description>
Built-in data label configuration is as follows.
({
labels: [
{
text,
dx: (d) => (d.x[0] < 0.5 ? spacing : -spacing),
...labelStyle, // User-provided custom data label styles
},
...nodeLabels, // User-provided custom data labels
],
});
Besides the built-in node data labels, you can also customize node data label configuration.
({
nodeLabels: [
{
text: (d) => d.key,
fontSize: 10, // Note!!! Drawing properties here no longer need the label prefix
fill: 'red',
},
],
});
linkLabels
<description>optional Label[] </description>
Links don’t have built-in data labels, but you can customize link data label configuration.
({
linkLabels: [
{
text: (d) => d.key,
fontSize: 10, // Note!!! Drawing properties here no longer need the label prefix
fill: 'yellow',
},
],
});
state
State configuration is similar to style, using different prefixes to distinguish different graphic configurations. Configurations without prefixes will apply to both graphics.
Example:
import * as G2 from '@antv/g2';
(() => {
const chart = new G2.Chart({ container: 'container' });
const data = {
links: [
{ source: 'a', target: 'b', value: 100 },
{ source: 'b', target: 'c', value: 80 },
{ source: 'b', target: 'd', value: 20 },
{ source: 'c', target: 'b_1', value: 80 },
{ source: 'b_1', target: 'c_1', value: 40 },
{ source: 'b_1', target: 'd_1', value: 40 },
],
};
chart.options({
type: 'sankey',
width: 900,
height: 600,
data: {
value: data,
},
style: {
labelSpacing: 3,
labelFontWeight: 'bold',
linkFillOpacity: 0.5,
nodeFillOpacity: 0.5, // Default opacity is 0.5
},
state: {
active: {
fillOpacity: 0.8, // Opacity is 0.8 on hover
linkFill: 'red', // Links turn red
nodeFill: 'blue', // Nodes turn blue
},
inactive: {
linkFillOpacity: 0.4,
nodeFillOpacity: 0.2, // Nodes are lighter than links
},
},
interaction: {
elementHighlight: true,
},
});
chart.render();
return chart.getContainer();
})();