Skip to content

Sankey and Chord

Flow series: edges fromField → toField weighted by sizeField.

Sankey

Nodes are laid out in columns by topological depth; link thickness is proportional to the flow.

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'User journey' },
    series: [{ type: 'sankey', fromField: 'from', toField: 'to', sizeField: 'value' }],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { from: 'Traffic', to: 'Organic', value: 620 },
    { from: 'Traffic', to: 'Ads', value: 380 },
    { from: 'Organic', to: 'Sign-up', value: 240 },
    { from: 'Ads', to: 'Sign-up', value: 190 },
    { from: 'Organic', to: 'Bounce', value: 380 },
    { from: 'Ads', to: 'Bounce', value: 190 },
    { from: 'Sign-up', to: 'Subscription', value: 160 },
    { from: 'Sign-up', to: 'Freemium', value: 270 },
  ];
}

Labels and node configuration

label (font, color, formatter({ name, total })), node.width/node.spacing, and linkOpacity:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Budget flow' },
    subtitle: { text: 'labels with totals, wide nodes, dense links' },
    series: [
      {
        type: 'sankey',
        fromField: 'from',
        toField: 'to',
        sizeField: 'amount',
        node: { width: 14, spacing: 24 },
        linkOpacity: 0.5,
        label: {
          fontSize: 12,
          fontWeight: 'bold',
          formatter: ({ name, total }) => `${name} · ${total}K`,
        },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { from: 'Salary', to: 'Budget', amount: 220 },
    { from: 'Freelance', to: 'Budget', amount: 60 },
    { from: 'Budget', to: 'Rent', amount: 90 },
    { from: 'Budget', to: 'Food', amount: 70 },
    { from: 'Budget', to: 'Transport', amount: 30 },
    { from: 'Budget', to: 'Savings', amount: 90 },
    { from: 'Savings', to: 'Investments', amount: 60 },
    { from: 'Savings', to: 'Emergency fund', amount: 30 },
  ];
}

Chord

Nodes around a circle, ribbons are the mutual flows.

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Calls between services' },
    series: [{ type: 'chord', fromField: 'from', toField: 'to', sizeField: 'calls' }],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { from: 'API', to: 'Auth', calls: 320 },
    { from: 'API', to: 'Billing', calls: 180 },
    { from: 'Web', to: 'API', calls: 540 },
    { from: 'Mobile', to: 'API', calls: 410 },
    { from: 'Billing', to: 'Auth', calls: 90 },
    { from: 'Web', to: 'Auth', calls: 130 },
  ];
}

Spacing and labels

nodeSpacing — the gap between arcs (px along the inner radius), linkOpacity — ribbon density, label.formatter receives the node's name and total:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'User migration between platforms' },
    subtitle: { text: 'nodeSpacing: 28, dense ribbons, labels with totals' },
    series: [
      {
        type: 'chord',
        fromField: 'from',
        toField: 'to',
        sizeField: 'users',
        nodeSpacing: 28,
        linkOpacity: 0.55,
        label: {
          fontSize: 12,
          formatter: ({ name, total }) => `${name} (${total})`,
        },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { from: 'Web', to: 'iOS', users: 18 },
    { from: 'Web', to: 'Android', users: 22 },
    { from: 'iOS', to: 'Web', users: 9 },
    { from: 'Android', to: 'Web', users: 12 },
    { from: 'iOS', to: 'Android', users: 6 },
    { from: 'Android', to: 'iOS', users: 7 },
    { from: 'Web', to: 'Desktop', users: 10 },
    { from: 'Desktop', to: 'Web', users: 5 },
  ];
}

Options

Options common to all series (name, showInLegend, tooltip.renderer, …) are covered in Common series options.

OptionSeriesDescription
fromFieldbothflow graph edges
toFieldbothflow graph edges
sizeFieldbothflow graph edges
fillsbothnode colors cycling through the palette

Full option list

OptionTypeDefaultDescription
linkOpacityboth0.35flow ribbon opacity
nodeSpacingchord12gap between node arcs, px
label.enabledbooleantruenode labels
label.formatter({ name, total }) => stringnode namecontent
label.fontSizePixels11label font size
label.fontWeightstring | numbernormalfont weight
label.fontFamilystringtheme fontfont family
label.colorColorValueforegroundcolor
node.widthPixels14sankey node width
node.spacingPixels14sankey node vertical gap