Sankey
Flow diagrams for visualizing weighted transitions between categories (energy flows, budget allocations, before/after correspondences, …). Built on d3-sankey with horizontal and vertical orientations, hover highlighting, and theming via CSS variables.
Basic Sankey
Pass data as { nodes, links }. Each link references nodes by id (defaults to array index) and carries a value that controls ribbon thickness. Node colors auto-cycle the default palette unless you set color.
svelte<script lang="ts">
import { Sankey, type SankeyData } from 'matterviz'
const data: SankeyData = {
nodes: [
{ label: `Coal` },
{ label: `Gas` },
{ label: `Solar` },
{ label: `Grid` },
{ label: `Homes` },
{ label: `Industry` },
],
links: [
{ source: `Coal`, target: `Grid`, value: 20 },
{ source: `Gas`, target: `Grid`, value: 15 },
{ source: `Solar`, target: `Grid`, value: 10 },
{ source: `Grid`, target: `Homes`, value: 25 },
{ source: `Grid`, target: `Industry`, value: 20 },
],
}
</script>
<Sankey {data} style="height: 380px" />Note nodes reference each other by label here because no explicit id is set, so the label doubles as the id. Set id explicitly when labels are not unique. Links must form a DAG: a cyclic graph renders an error naming the cycle (e.g. Grid -> Homes -> Grid) in place of the diagram, and compute_sankey_layout throws the same message.
Multi-layer flow (5 levels)
The layout handles arbitrary multi-layer DAGs. Node columns are derived from the graph
topology, so this strictly layered energy-flow graph (sources to conversion to carrier to
distribution to end use) renders as five columns. Flows are conserved at every node
(value in == value out).
svelte<script lang="ts">
import { Sankey, type SankeyData } from 'matterviz'
const data: SankeyData = {
nodes: [
// L0 sources
{ label: `Coal`, color: `#6b6b6b` },
{ label: `Gas`, color: `#9c755f` },
{ label: `Nuclear`, color: `#af7aa1` },
{ label: `Wind`, color: `#76b7b2` },
{ label: `Solar`, color: `#edc949` },
// L1 conversion
{ label: `Power Plants`, color: `#e15759` },
{ label: `Renewable Farms`, color: `#59a14f` },
// L2 carrier
{ label: `Electricity`, color: `#4e79a7` },
{ label: `Heat`, color: `#f28e2c` },
// L3 distribution
{ label: `Grid`, color: `#4e79a7` },
{ label: `District Heating`, color: `#f28e2c` },
// L4 end use
{ label: `Residential`, color: `#59a14f` },
{ label: `Industry`, color: `#e15759` },
{ label: `Transport`, color: `#76b7b2` },
{ label: `Commercial`, color: `#edc949` },
],
links: [
{ source: `Coal`, target: `Power Plants`, value: 30 },
{ source: `Gas`, target: `Power Plants`, value: 25 },
{ source: `Nuclear`, target: `Power Plants`, value: 20 },
{ source: `Wind`, target: `Renewable Farms`, value: 18 },
{ source: `Solar`, target: `Renewable Farms`, value: 12 },
{ source: `Power Plants`, target: `Electricity`, value: 55 },
{ source: `Power Plants`, target: `Heat`, value: 20 },
{ source: `Renewable Farms`, target: `Electricity`, value: 30 },
{ source: `Electricity`, target: `Grid`, value: 85 },
{ source: `Heat`, target: `District Heating`, value: 20 },
{ source: `Grid`, target: `Residential`, value: 30 },
{ source: `Grid`, target: `Industry`, value: 35 },
{ source: `Grid`, target: `Transport`, value: 10 },
{ source: `Grid`, target: `Commercial`, value: 10 },
{ source: `District Heating`, target: `Residential`, value: 12 },
{ source: `District Heating`, target: `Industry`, value: 8 },
],
}
</script>
<Sankey {data} link_color_mode="gradient" value_format="," style="height: 520px" />Branching funnel (vertical, 4 levels)
A materials-screening funnel with drop-offs at each stage. Nodes without outgoing links
(dead ends like Unstable) would be pushed to the last column under the default justify alignment, so funnels read better with node_align="left", which positions every node by
its depth from the sources.
svelte<script lang="ts">
import { Sankey, type SankeyData } from 'matterviz'
const data: SankeyData = {
nodes: [
{ label: `Candidates`, color: `#4e79a7` },
{ label: `Stable`, color: `#59a14f` },
{ label: `Unstable`, color: `#e15759` },
{ label: `Synthesizable`, color: `#76b7b2` },
{ label: `Metastable`, color: `#edc949` },
{ label: `Experimentally Made`, color: `#59a14f` },
{ label: `Predicted Only`, color: `#af7aa1` },
],
links: [
{ source: `Candidates`, target: `Stable`, value: 700 },
{ source: `Candidates`, target: `Unstable`, value: 300 },
{ source: `Stable`, target: `Synthesizable`, value: 500 },
{ source: `Stable`, target: `Metastable`, value: 200 },
{ source: `Synthesizable`, target: `Experimentally Made`, value: 350 },
{ source: `Synthesizable`, target: `Predicted Only`, value: 150 },
{ source: `Metastable`, target: `Predicted Only`, value: 200 },
],
}
</script>
<Sankey
{data}
orientation="vertical"
node_align="left"
value_format=","
style="height: 520px"
/>Orientation, alignment and link coloring
All layout knobs are reactive props (also exposed in the settings pane): orientation, node_align, node_width, node_padding, link_opacity, link_color_mode (source | target | gradient | static) and show_node_labels.
svelte<script lang="ts">
import { Sankey, type SankeyData, type SankeyLinkColorMode } from 'matterviz'
let orientation = $state<'horizontal' | 'vertical'>(`horizontal`)
let link_color_mode = $state<SankeyLinkColorMode>(`gradient`)
const data: SankeyData = {
nodes: [
{ label: `A`, color: `#4e79a7` },
{ label: `B`, color: `#f28e2c` },
{ label: `X`, color: `#59a14f` },
{ label: `Y`, color: `#e15759` },
{ label: `Out`, color: `#b07aa1` },
],
links: [
{ source: `A`, target: `X`, value: 6 },
{ source: `A`, target: `Y`, value: 4 },
{ source: `B`, target: `X`, value: 3 },
{ source: `B`, target: `Y`, value: 7 },
{ source: `X`, target: `Out`, value: 9 },
{ source: `Y`, target: `Out`, value: 11 },
],
}
</script>
<div style="display: flex; gap: 1em; margin-bottom: 1em; flex-wrap: wrap">
<label
>Orientation:
<select bind:value={orientation}>
<option value="horizontal">Horizontal</option>
<option value="vertical">Vertical</option>
</select>
</label>
<label
>Link color:
<select bind:value={link_color_mode}>
<option value="source">Source</option>
<option value="target">Target</option>
<option value="gradient">Gradient</option>
<option value="static">Static</option>
</select>
</label>
</div>
<Sankey {data} {orientation} {link_color_mode} style="height: 420px" />Bipartite diagram from flat arrays
sankey_from_links(source, target, value, labels?) builds SankeyData from the parallel
index arrays produced by tools like Plotly/pymatviz (e.g. matbench-discovery’s DFT-vs-ML
spacegroup correspondence diagrams). source/target are zero-based node indices.
svelte<script lang="ts">
import { Sankey, sankey_from_links } from 'matterviz'
// DFT spacegroup (left) -> ML-relaxed spacegroup (right)
const data = sankey_from_links(
[0, 1, 2, 0, 1, 2],
[3, 3, 4, 4, 5, 5],
[1750, 820, 610, 290, 130, 70],
[`P1`, `P2₁/c`, `Fm-3m`, `Pnma`, `R-3m`, `C2/m`],
)
</script>
<Sankey {data} node_align="left" value_format="," style="height: 360px" />Custom tooltip
The tooltip snippet receives the hovered node or link (SankeyHandlerProps, discriminated by type).
svelte<script lang="ts">
import { Sankey, type SankeyData } from 'matterviz'
const data: SankeyData = {
nodes: [{ label: `Input` }, { label: `Loss` }, { label: `Output` }],
links: [
{ source: `Input`, target: `Loss`, value: 18 },
{ source: `Input`, target: `Output`, value: 82 },
],
}
</script>
<Sankey {data} style="height: 300px">
{#snippet tooltip(info)}
{#if info.type === `node`}
<strong>{info.label}</strong>: {info.value} units
{:else}
{info.source_label} → {info.target_label}: <strong>{info.value}</strong>
{/if}
{/snippet}
</Sankey>