SankeyChart
A typed Sankey flow diagram — nodes + links, width proportional to value.
Usage
import { SankeyChart } from '@acronis-platform/ui-react';
import type { ChartConfig } from '@acronis-platform/ui-react';SankeyChart is a typed composition over the shared Chart
primitives, built on recharts' Sankey. Give it a graph (data.nodes +
data.links) and a per-node config — it renders a themed flow diagram where
each link's width is proportional to its value and each ribbon is tinted by its
target node, so you don't hand-compose recharts children. There are no axes
or grid; a count/% legend is opt-in via showLegend (see below).
Node colors come from the palette prop, not from config — see
Palettes. Node names are the color keys, keyed
into config: each entry maps a node to a label and an optional tone that
re-points it within the palette, and SankeyChart resolves them into the
--color-<name> custom properties the node bars fill from. Names must be unique
and CSS-safe.
Examples
A flow of nodes and links, ribbon width proportional to value:
const data = {
nodes: [{ name: 'all' }, { name: 'certified' }, { name: 'expired' }],
links: [
{ source: 0, target: 1, value: 209 },
{ source: 0, target: 2, value: 31 },
],
};
const config = {
all: { label: 'All tenants', tone: { status: 'info' } },
certified: { label: 'Certified', tone: { status: 'info' } },
expired: { label: 'Expired', tone: { status: 'danger' } },
} satisfies ChartConfig;
<SankeyChart
config={config}
palette={{ type: 'status' }}
data={data}
className="h-[360px] w-[620px]"
/>;Tune the geometry with nodePadding / nodeWidth / linkCurvature, auto-order
nodes with sort, hide the on-chart labels with showLabels, and show a
built-in legend (color swatch + label + value + %) with showLegend:
<SankeyChart
config={config}
data={data}
nodeWidth={16}
linkCurvature={0.6}
showLabels={false}
showLegend
/>;showLegend provides that count/% legend (each node's value + share of the
largest node) — the "Certification compliance" card is just showLabels={false}
showLegend. Give a link an explicitcolorto override its default target tint; an explicit color renders full-opacity to match its node bar, while the default tinted ribbons stay at 35%:
const data = {
nodes: [{ name: 'all' }, { name: 'certified' }],
links: [
{ source: 0, target: 1, value: 209, color: 'var(--ui-background-brand-primary-disabled)' },
],
};Tooltip
showTooltip (on by default) shows a hover card for the hovered node or link —
a color dot matching the ribbon, the source → target config labels, and the
value. Set showTooltip={false} to drop it, or pass tooltipContent to replace
the card's content with your own render function while keeping the same trigger
behavior:
<SankeyChart
config={config}
data={data}
tooltipContent={({ active, payload }) =>
active && payload?.length ? (
<div className="rounded-lg border border-border bg-background px-2.5 py-1.5 text-xs">
{Number(payload[0]?.value).toLocaleString()} users
</div>
) : null
}
/>;The tooltip is pointer-only: recharts' Sankey has no accessibility layer (no
keyboard focus), so keep the values reachable in text — showLegend, a caption,
or an adjacent table — rather than only on hover.
Accessibility
SankeyChart sets no role and no aria-* of its own, and recharts draws a Sankey
into a bare <svg> — an empty <title> / <desc> pair, no role, no tabindex. So
the diagram has no accessible name, the plot is not a tab stop, and (as the tooltip
note above says) the hover card is a reading a keyboard never reaches.
The root is a plain <div> that spreads the props you pass, so a role and a label
land on it:
<SankeyChart
role="img"
aria-label="Certification status of 240 tenants: 209 certified, 31 expired"
config={config}
data={data}
/>aria-labelledby works the same way, as does wrapping the chart in a <figure> with
a <figcaption>.
A name is not a text alternative, though. A flow diagram is a visual encoding of
numbers, so the numbers themselves have to be reachable as text — a caption, a
summary sentence, or a table beside the chart. showLegend is the cheapest route:
it renders each node's label, value and share as ordinary HTML below the plot rather
than inside the SVG. The node labels showLabels draws are real <text> and not
pixels, but they sit in the SVG with no structural role and carry no value alongside
the name.
Direction (RTL)
Under dir="rtl" the chrome around the plot — the tooltip, SankeyChart's own
legend (plain DOM below the diagram), and any readouts you compose alongside them —
mirrors on its own, because it is ordinary flow layout built from logical CSS
utilities. The plot area deliberately stays left-to-right: recharts places every mark
in physical SVG coordinates, so mirroring it would flip the reading order of the data
without flipping the geometry that encodes it. ChartContainer holds that in place
by pinning .recharts-surface to direction: ltr.
For a Sankey that pin fixes the labels as much as the diagram. The node renderer
anchors each label with text-anchor: start or end depending on whether the node
has outgoing flow, and those two SVG keywords resolve against the inherited
direction — so without the pin an inherited dir="rtl" would mirror every label
back across its anchor and lay it over the node it belongs to. With it, the columns
run from source to sink left to right and a node's label sits on the side its links
choose — to the right of a node with outgoing flow, to the left of a terminal one —
which is a property of the flow, not of the reading direction. The pin stops at the
surface, so the tooltip and the legend still mirror with the page.
API Reference
Prop
Type