Sankey
PreviewSVGPublishes selection
Shows how a quantity flows from one set of categories to another, such as traffic channel to outcome.
Playground
Sessions by channel and outcome
Sep 7 – Oct 6, 2026
Flows from Channel to Outcome: total 80,870 across 12 paths. Largest: Paid social → Bounced (16,800), Organic search → Bounced (12,400), Paid social → Abandoned cart (10,100).
| Channel | Outcome | Sessions |
|---|---|---|
| Organic search | Purchased | 5,840 |
| Organic search | Abandoned cart | 9,210 |
| Organic search | Bounced | 12,400 |
| Paid social | Purchased | 5,480 |
| Paid social | Abandoned cart | 10,100 |
| Paid social | Bounced | 16,800 |
| Purchased | 4,150 | |
| Abandoned cart | 3,900 | |
| Bounced | 3,100 | |
| Direct | Purchased | 2,790 |
| Direct | Abandoned cart | 2,400 |
| Direct | Bounced | 4,700 |
nodeWidth
<Sankey
data={channelFlows}
from="channel"
to="outcome"
value="sessions"
select
/>Installation
Sankey is exported from @quartile/react, alongside every other chart and component.
git clone https://github.com/zinnoberHaus/quartileNot on npm yet: build the package from the clone as described in Installation. The preview API may change before publication.
Usage
One row per flow; rows that share a pair are summed. A name used in both from and to links the stages.
import { Sankey } from '@quartile/react';
const flows = [
{ channel: 'Email', outcome: 'Purchased', sessions: 4150 },
{ channel: 'Email', outcome: 'Bounced', sessions: 6200 },
{ channel: 'Paid social', outcome: 'Purchased', sessions: 5480 },
];
export function ChannelOutcomes() {
return <Sankey data={flows} from="channel" to="outcome" value="sessions" select />;
}With
select, clicking a node (or Enter on it) toggles it in the selection on the from or to field; Shift adds. See Linked selections.Props
PropTypeDefaultDescription
datarequiredDataInput<R>—Rows of flows (aggregated per pair), or a dataset with a schema attached.
fromrequiredkeyof R & string—Field naming where a flow starts.
torequiredkeyof R & string—Field naming where a flow ends. A name used in both fields links the stages.
valuekeyof R & string—Flow size. Counts rows when omitted.
formatFormatter—Formats flow sizes. Defaults to the field's schema format.
nodeWidthnumber14Node bar width in px.
nodePaddingnumber10Vertical gap between nodes in px.
selectbooleanfalseClick (or Enter) on a node toggles it in the selection on
from or to. Shift adds.idstring—Publisher id for the selection. Defaults to a generated id.
selectionstring | false—Selection to read from and publish to. Defaults to the nearest; false opts out.
onSelect(predicate: Predicate | null) => void——
Frame and state props shared with the charts (11) · ChartBaseProps, ChartStateProps
PropTypeDefaultDescription
heightnumber220Plot height in px. Width always fills the container.
classNamestring——
styleCSSProperties——
aria-labelstring—Accessible name. A summary of the data is generated and rendered as hidden text inside the figure.
view'chart' | 'table'—
table renders the same data as a table, with the same formats, at the chart's size. Large lazy tables expose a data-view control rather than mounting every hidden row.tooltipNotestring—Plain-text context shown with tooltips and included in the accessible chart description.
loadingboolean | string—Shows the loading state at the chart's size, so the layout never jumps.
errorboolean | string | Error | null—Shows the error state. A string or Error becomes the message.
errorCodestring—Shown in the error state; copyable for support.
onRetry() => void——
empty{ title?: string; description?: string; action?: ReactNode }—Title and description for the empty state. Shown when there are no rows.
Accessibility
Keyboard
Tab into the diagram; arrow keys move through nodes and then flows. With
select, Enter or Space toggles the focused node and Shift adds it.Screen readers
A summary gives the total, the number of paths and the largest flows. The focused node or flow is announced as you move.
Table fallback
Screen readers always get a table of the plotted values with the same formats.
view="table" shows it at the chart's size.Report an issueBuilt Oct 21, 2026