Sankey Diagrams in Python
A Sankey diagram shows how a quantity flows through a sequence of stages. Node height represents total flow, and each connecting ribbon is proportional to its value. Use one for budgets, energy transfer, conversion funnels, supply chains, or any directed flow where the size of each path matters.
With xy, pass (source, target, value) triples to sankey_chart. XY assigns
layers, minimizes crossings, sizes the nodes, stacks the ribbon endpoints, and
uses each link's source and target colors to paint its gradient.
Jump to the basic chart, a dense energy network, sink alignment, or custom ribbon styling.
Create a Sankey Diagram
This example follows an investment inflow through allocations and outcomes:
Node names default to their first-appearance order in links. When you need
stable ordering independent of the input rows, pass every name through
nodes=:
The colors sequence follows that same node order and must contain exactly one
CSS color per node.
Trace a Dense Energy Network
Sankey diagrams are most useful when several branches split and rejoin. This
energy balance uses four stages, a compact node_padding, and extra
crossing-minimization iterations. Every ribbon blends from its source node
color to its target node color:
Use an explicit nodes list when color meaning must remain stable even if the
input rows are reordered. More iterations can improve a busy layout, though
the crossing-minimization algorithm is heuristic rather than guaranteed to
find the mathematical optimum.
Compare Sink Alignment
A sink is a node with no outgoing link. With align="left", an early sink
stays in the first layer where the graph places it. The default
align="justify" moves every sink to the final layer, producing a flush right
edge. Compare the position of Direct purchase:
For a single-column mobile layout, place the two chart components in an
rx.vstack or make the grid's columns prop responsive in your Reflex app.
Style the Ribbon Layer
Use the xy.sankey mark directly when you want mark-level styling. The style
mapping below adds a subtle purple outline, while high-opacity links preserve
the intended lavender-to-violet gradient without competing with the solid node
bars. Composing the mark yourself also means supplying the hidden unit-box axes
that sankey_chart normally adds for you:
The mark's style mapping applies to links, while colors controls nodes and
the two ends of each link gradient. Here, pale lavender sources transition
through a saturated purple process hub to deep-violet outcomes, reinforcing
the left-to-right flow. Wider, fully opaque node bars sit above slightly dimmed
ribbons, so every stage remains distinct instead of blending into its attached
flows. Sankey names use the chart's annotation_label style slot; a light
color and dark shadow keep labels readable across changing ribbon colors. Set
labels=False for a compact, label-free diagram, or increase label_size when
the chart has room for larger text.
Layout and Flow Rules
Sankey links form a directed acyclic graph: every path moves from an earlier stage to a later stage. XY rejects cycles and names the nodes involved instead of drawing a misleading backward flow.
Each (source, target) pair must appear once. Aggregate repeated pairs before
passing them to the chart, and use finite, non-negative values. Node height is
the larger of total inflow and total outflow, so a node remains large enough
for every ribbon attached to it.
align="justify" places terminal nodes on the final layer. The other supported
alignments are "left", "right", and "center": left keeps each node in the
earliest layer its links allow, right hangs each node by its distance to a
sink, and center moves nodes without incoming links next to their first
target. Alternating barycenter sweeps reduce crossings; increase iterations
for a denser graph when the extra layout work improves the result.
Sankey Options
Chart options such as width, height, title, and theme settings are passed
to sankey_chart alongside the Sankey options.
Interaction and Export
The browser resolves ribbon hover by testing the pointer against the same curved band geometry used for rendering. Link tooltips show source → target with the flow value; node tooltips show the node name and its total flow. Sankey diagrams also export through the standard chart methods:
GPU picking, ribbon hover highlighting, automatic legend swatches for two-color ribbons, cycle breaking, and Sankey-specific level of detail are not implemented yet.
Related Charts
- Bar and column charts compare independent category totals without encoding a flow between them.
- Segments draw independent point-to-point connections when band width does not carry a value.
- Annotations add explanatory labels, callouts, and thresholds around a flow diagram.
API Reference
xy.sankey_chart
A Sankey diagram chart: flow layout, gradient ribbons, hidden axes.
Props
| Prop | Type | Description |
|---|---|---|
links | Any | Defaults to None. |
*children | Component | Marks, axes, annotations, and chart chrome. |
title | Optional[str] | Title shown above the plot. |
width | int | str | Chart width in pixels or a CSS size such as ``"100%"``. |
height | int | str | Chart height in pixels or a CSS size such as ``"100%"``. |
padding | Union[float, Sequence[float], None] | Plot margins, as one value or a sequence of side values. Use zero for an edge-to-edge sparkline. |
data | TableLike | Chart-level data used by marks that omit their own ``data``. |
class_name | Optional[str] | CSS class applied to the chart container. |
class_names | Optional[dict[str, str]] | CSS classes keyed by stable chart DOM slot. |
style | Optional[dict[str, StyleValue]] | Inline style overrides for the chart container. |
styles | Optional[dict[str, dict[str, StyleValue]]] | Inline style mappings keyed by stable chart DOM slot. |
on_hover | Optional[Callable[[dict], None]] | Callback receiving hover event payloads. |
on_click | Optional[Callable[[dict], None]] | Callback receiving picked-mark click payloads. |
on_brush | Optional[Callable[[dict], None]] | Callback receiving brush event payloads. |
on_select | Optional[Callable[[Selection], None]] | Callback receiving data-space selections. |
on_view_change | Optional[Callable[[dict], None]] | Callback receiving viewport change payloads. |
hover | Optional[bool] | Whether pointer movement emits hover events. |
click | Optional[bool] | Whether picked marks emit click events. |
select | Optional[bool] | Whether shift-drag box selection is enabled. |
brush | Optional[bool] | Whether brush selection is enabled. |
crosshair | Optional[bool] | Whether plot-aligned hover guides are shown. |
navigation | Optional[bool] | Whether browser pan and zoom navigation is enabled. |
pan | Optional[bool] | Whether plain-drag panning is enabled. |
pan_axes | Optional[tuple[str, ...]] | Declared axis IDs translated by pan gestures. |
zoom | Optional[bool] | Whether viewport zoom is enabled. |
default_drag_action | Optional[DefaultDragAction] | Initial action performed by a plain plot drag. |
zoom_axes | Optional[tuple[str, ...]] | Declared axis IDs changed by zoom gestures and controls. |
zoom_limits | Optional[ZoomLimits] | Minimum and maximum magnification globally or by axis. |
wheel_zoom | Optional[bool] | Whether wheel and trackpad zoom is available. |
box_zoom | Optional[bool] | Whether box zoom is available as a drag action. |
zoom_buttons | Optional[bool] | Whether modebar Zoom In/Out commands are available. |
double_click_reset | Optional[bool] | Whether double-click restores ``reset_axes``. |
reset_axes | Optional[tuple[str, ...]] | Declared axis IDs restored by reset. |
link_group | Optional[str] | Identifier used to synchronize charts in the browser. |
link_axes | Optional[tuple[str, ...]] | Axes synchronized within the link group. |
coords | str | Coordinate system, ``"cartesian"`` (default) or ``"polar"``. Under ``"polar"`` each mark's first channel is the angle and its second is the radius. Prefer ``xy.polar_chart(...)``, which sets this for you. |
FAQ
How do I create a Sankey diagram in Python?
Pass (source, target, value) triples to xy.sankey_chart(...). XY computes
the node layers, sizes, ordering, and ribbon endpoints automatically.
Can a Sankey diagram contain cycles?
No. A Sankey flows from earlier stages to later stages. XY raises an error that names the nodes in a cycle so you can break or aggregate that loop explicitly.
How do I control Sankey colors?
Pass one CSS color per node through colors=. The source and target colors are
interpolated across each ribbon to make its direction readable.
How do I move terminal nodes to the right edge?
Use the default align="justify". Choose "left", "right", or "center"
when a different layer alignment better matches the story in your data.