Sankey Chart
A composable sankey diagram for visualizing flow between nodes with animated links and interactive tooltips
Preview
Installation
pnpm dlx shadcn@latest add https://ui.bklit.com/r/sankey-chart.jsonUsage
The Sankey Chart uses a composable API similar to other charts. Build diagrams by combining components:
import { SankeyChart, SankeyNode, SankeyLink, SankeyTooltip } from "@bklitui/ui/charts";
const data = {
nodes: [
{ name: "Organic Search", category: "source" },
{ name: "Homepage", category: "landing" },
{ name: "Converted", category: "outcome" },
],
links: [
{ source: 0, target: 1, value: 100 },
{ source: 1, target: 2, value: 80 },
],
};
export default function FlowDiagram() {
return (
<SankeyChart data={data}>
<SankeyLink />
<SankeyNode lineCap={4} />
<SankeyTooltip />
</SankeyChart>
);
}Components
SankeyChart
The root component that computes the layout and provides context to children.
| Prop | Type | Default | Description |
|---|---|---|---|
data | SankeyData | required | Object with nodes and links arrays |
margin | Partial<Margin> | { top: 40, right: 180, bottom: 40, left: 180 } | Chart margins |
animationDuration | number | 1100 | Animation duration in ms |
aspectRatio | string | "2 / 1" | CSS aspect ratio |
nodeWidth | number | 16 | Width of nodes in pixels |
nodePadding | number | 24 | Vertical padding between nodes |
className | string | "" | Additional CSS class |
SankeyNode
Renders the nodes (bars) in the diagram with animated initialization and internal labels.
| Prop | Type | Default | Description |
|---|---|---|---|
fill | string | - | Fill color for all nodes |
lineCap | number | 4 | Corner radius for nodes |
fadedOpacity | number | 0.4 | Opacity when another element is hovered |
showLabels | boolean | true | Show node name and value labels |
getNodeColor | (node, index) => string | - | Custom color function |
SankeyLink
Renders the links (flows) between nodes with animated path reveal and gradient colors.
| Prop | Type | Default | Description |
|---|---|---|---|
stroke | string | - | Solid stroke color (overrides gradient) |
strokeOpacity | number | 0.7 | Link opacity |
fadedOpacity | number | 0.1 | Opacity when another element is hovered |
useGradient | boolean | true | Use gradient from source to target node color |
getNodeColor | (node, index) => string | - | Custom node color function for gradients |
getLinkColor | (link, index) => string | - | Custom link color (overrides gradient) |
patterns | ReactNode | - | Pattern definitions using @visx/pattern components |
getLinkPattern | (link, index) => string | null | - | Return pattern ID for a link, or null for gradient |
SankeyTooltip
Displays tooltips for nodes and links on hover.
| Prop | Type | Default | Description |
|---|---|---|---|
nodeContent | (props) => ReactNode | - | Custom node tooltip renderer |
linkContent | (props) => ReactNode | - | Custom link tooltip renderer |
formatValue | (value) => string | toLocaleString | Value formatter |
className | string | "" | Additional CSS class |
Data Format
The sankey data follows the d3-sankey format:
interface SankeyData {
nodes: Array<{
name: string;
category: "source" | "landing" | "outcome";
[key: string]: unknown;
}>;
links: Array<{
source: number; // Index into nodes array
target: number; // Index into nodes array
value: number; // Flow value
}>;
}Animation
The sankey chart uses the same animation system as other charts:
- Nodes: ScaleY and opacity fade in, staggered by index
- Links: Grow from source to target node using stroke-dashoffset animation
- Gradients: Links use gradient colors flowing from source node color to target node color
- Easing:
cubic-bezier(0.85, 0, 0.15, 1)for smooth, organic motion
Animation Timeline
- 0-600ms: Nodes fade/scale in (staggered)
- 200-1100ms: Links grow from source to target (staggered, starts after nodes begin appearing)
Hover Behavior
When hovering over a node or link:
- Hovered link and its source/target nodes stay at full opacity
- Connected nodes and links remain visible
- All other links and nodes fade
- Tooltip appears showing relevant data
Examples
Without Labels
For a cleaner look, hide the node labels:
<SankeyChart data={analyticsData} aspectRatio="16 / 9" nodeWidth={16} nodePadding={24} margin={{ top: 20, right: 20, bottom: 20, left: 20 }}><SankeyLink /><SankeyNode lineCap={4} showLabels={false} /><SankeyTooltip /></SankeyChart>With Patterns
Use @visx/pattern to style specific links with patterns instead of gradients:
<SankeyChart data={analyticsData} aspectRatio="16 / 9" nodeWidth={16} nodePadding={24}><SankeyLink getLinkPattern={getOutcomePattern} patterns={ <> <PatternLines id="converted" stroke="#22c55e" ... /> <PatternLines id="engaged" stroke="#eab308" ... /> <PatternLines id="bounced" stroke="#ef4444" ... /> </> }/><SankeyNode lineCap={4} /><SankeyTooltip /></SankeyChart>Dependencies
This component requires:
pnpm add @visx/sankey @visx/responsive @visx/pattern motion react-use-measure