Usage
Sankey data points define directed flows between named nodes.
{ from: 'Coal', to: 'Electricity', flow: 42}Common Dataset Options
Section titled “Common Dataset Options”| Option | Description |
|---|---|
alpha | Opacity applied to colorFrom and colorTo when rendering a flow. Not scriptable. Defaults to 0.5. |
colorFrom | Scriptable color for the source node side. |
colorMode | Flow coloring mode: gradient, from, or to. |
colorTo | Scriptable color for the target node side. |
column | Optional column assignment by node key. |
flowColor | Scriptable flow color. When set, it overrides colorMode for flows without changing node colors. |
flowLabels | Optional flow-value labels with scriptable styling and placement. See Flow Labels. |
hoverColorFrom | Scriptable hover color for the source node side. Defaults to colorFrom saturated and darkened. |
hoverColorTo | Scriptable hover color for the target node side. Defaults to colorTo saturated and darkened. |
labels | Optional display labels by node key. |
nodeLabels | Node label styling and placement. Positions are auto, left, top, right, bottom, or center. See Node Labels. |
nodeMinSize | Minimum drawn node bar size, in CSS pixels. Resolved per node; see Node Min Size. Defaults to 0 (no minimum). |
nodePadding | Requested vertical gap between nodes within a column, in CSS pixels — measured before the chart is scaled to fit, so the rendered gap is approximate. Resolved per node; see Node Padding. |
nodePaddingMode | auto (default) or even. See Node Padding Mode for the trade-off. |
nodeWidth | Node rectangle width in pixels. |
orientation | Flow direction: horizontal (left to right, the default) or vertical (top to bottom). |
priority | Optional ordering priority by node key. |
size | Node size strategy: min or max. |
hoverColorFrom and hoverColorTo are never simply unset: leaving them out doesn’t turn off the hover effect, it falls back to a computed color — colorFrom/colorTo run through Chart.js’s own getHoverColor helper (saturated by 0.5, darkened by 0.1). To get no color change on hover, set hoverColorFrom/hoverColorTo explicitly to the same value as colorFrom/colorTo.
Node Labels
Section titled “Node Labels”Use nodeLabels to place and style labels globally or by node. Position names follow Chart.js layout positions, with auto preserving Sankey’s automatic left/right placement.
nodeLabels: { position: { Coal: 'right', Electricity: 'center', Energy: 'left' }, color: (node) => node.key === 'Electricity' ? 'white' : 'black', backgroundColor: { Electricity: 'blue' }, borderRadius: 3, padding: 4, font: { size: 11, weight: 'normal' }}Node Padding
Section titled “Node Padding”nodePadding sets the vertical gap between nodes stacked in the same column, in CSS pixels — but it’s a request, not an exact distance: the value is measured before the chart is scaled to fit its drawing area, so the gap that actually renders is approximate rather than exact. In auto mode it typically renders a little smaller than requested, narrowing further the more gaps a column has (a nodePadding: 10 renders as roughly 9px in a 400px-tall chart with five nodes in the column). In even mode a column packs more tightly instead, so the rendered gap can land at or close to the requested value — see Node Padding Mode for why. Neither mode promises a direction in general. The ratio between different nodePadding values you set is preserved either way. Like nodeLabels, it resolves per node, in three forms:
- A plain number applies the same gap everywhere:
nodePadding: 20. - A
{ before, after }object sets an asymmetric gap: more space above the node than below it, or vice versa.beforeis the gap above the node andafteris the gap below it, regardless oforientation(in a vertical chart, “above”/“below” become “left”/“right” visually, butbefore/afterkeep the same meaning). Either side left out falls back to10. - A function receiving the node and returning either form, for full control:
nodePadding: (node) => node.key === 'Coal' ? { after: 28 } : 4.
A top-level object is always a Record of node keys (like nodeLabels), never a { before, after } gap directly — that shape would be ambiguous with a keyed Record. Write a function for a global asymmetric gap instead:
// per-node gaps, keyed by nodenodePadding: { Coal: { after: 28 }, Solar: 4}
// a global asymmetric gap: more space below every node than above itnodePadding: () => ({ before: 4, after: 8 })Gaps collapse like CSS margins instead of summing: the space between two adjacent nodes in a column is max(prevNode.after, nextNode.before), not their sum. A plain nodePadding: 10 is therefore equivalent to every node using { before: 10, after: 10 } — max(10, 10) is still 10.
Node Padding Mode
Section titled “Node Padding Mode”By default (nodePaddingMode: 'auto'), a node’s vertical position also has to clear every node stacked above it in the columns feeding into it, so it stays aligned with the flows arriving from the left. That alignment is the point, but it has a side effect: two nodes in the same column can end up with visibly different gaps above them, depending on how crowded the columns to their left are. Setting nodePaddingMode: 'even' instead stacks a column’s nodes back to back, top node’s position unchanged, each following node placed at exactly max(prevNode.after, nextNode.before) below the previous one — regular gaps, but the column’s nodes are no longer aligned with their incoming flows, so those flows bend more to reach them. Note also that size: 'min' can make a node’s drawn bar smaller than the flows it carries, so flows can still overlap visually in even mode exactly as they can today in auto mode.
See the Node Padding sample for a single wide gap separating groups within a column, and for auto vs. even side by side.
Node Min Size
Section titled “Node Min Size”nodeMinSize gives a node’s drawn bar a minimum size in CSS pixels, so a node carrying a tiny flow next to a huge one doesn’t disappear into a sub-pixel line. It resolves per node, in the same three forms as nodePadding: a plain number (nodeMinSize: 6), a Record keyed by node key, or a function receiving the node. Defaults to 0, meaning no minimum.
Only the drawn bar is stretched — the underlying data isn’t. When a node’s natural size (in flow units, converted to pixels) is smaller than nodeMinSize, its bar grows to nodeMinSize symmetrically around the node’s real position: half the growth extends above/left, half below/right. The node’s label follows the same stretched rectangle. The flows attached to the node keep their exact proportional height and still connect at the same points they always did — stretching the bar doesn’t move or resize any flow, so a bar that grew to be visible can still have flows that are their own hairline thickness inside it.
Space for the stretch is reserved during layout, so a stretched bar does not overlap a neighboring node — the gap between two stretched bars in the same column still respects the requested nodePadding, in both auto and even mode.
nodeMinSize makes the node visible, not the flow. A flow of 0.37 arriving at a 6px node bar is still a hairline inside that bar — if the flow itself needs to be visible, the data needs a minimum applied before charting (e.g. clamped upstream), and that’s usually worth calling out in a label rather than silently distorting the chart.
See the Node Min Size sample for a huge node next to several tiny ones, with and without a minimum.
Flow Labels
Section titled “Flow Labels”Flow labels use the same visual options and display the numeric flow value. Since flows are Chart.js elements, these options use the standard scriptable context.
flowLabels: { display: (context) => context.raw.flow >= 10, position: 'center', color: 'black', backgroundColor: 'white', borderRadius: 3, padding: 4, font: { size: 11, weight: 'normal' }}