Skip to content

Sunburst

Rings per nesting level, with the angle proportional to the value. Data is nested via children; a node's value is the leaf's sizeField or the sum of its descendants.

Every ring of a branch carries that branch's colour at full strength — a sector further out is deeper, not fainter — and one ring is told from the next by the gap between them. Hovering lifts a sector towards its own contrast colour instead of paling the others, the way a treemap tile does.

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

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Users by region' },
    subtitle: { text: 'millions, two levels' },
    series: [{ type: 'sunburst', labelField: 'label', sizeField: 'size' }],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    {
      label: 'Europe',
      children: [
        { label: 'Germany', size: 84 },
        { label: 'France', size: 65 },
        { label: 'Poland', size: 38 },
      ],
    },
    {
      label: 'Asia',
      children: [
        { label: 'Japan', size: 125 },
        { label: 'Korea', size: 52 },
        { label: 'Vietnam', size: 98 },
      ],
    },
    { label: 'Other', size: 75 },
  ];
}

Spacing and corner rounding

sectorSpacing is a constant-width gap between sectors (like in pie), cornerRadius rounds the corners:

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

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Sector spacing and corner radius' },
    series: [
      {
        type: 'sunburst',
        labelField: 'label',
        sizeField: 'size',
        sectorSpacing: 4,
        cornerRadius: 5,
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    {
      label: 'Product',
      children: [
        { label: 'Web', size: 34 },
        { label: 'iOS', size: 22 },
        { label: 'Android', size: 18 },
      ],
    },
    {
      label: 'Platform',
      children: [
        { label: 'API', size: 26 },
        { label: 'Data', size: 16 },
      ],
    },
    {
      label: 'Support',
      children: [
        { label: 'SLA', size: 12 },
        { label: 'Docs', size: 8 },
      ],
    },
  ];
}

Sector labels

label: { enabled: true } renders labels in the sectors they fit into; the color is auto contrast, and formatter receives label, value, and depth:

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

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Sector labels' },
    series: [
      {
        type: 'sunburst',
        labelField: 'label',
        sizeField: 'size',
        sectorSpacing: 2,
        label: {
          enabled: true,
          formatter: ({ label, value, depth }) => (depth === 0 ? label : `${label} · ${value}`),
        },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    {
      label: 'Product',
      children: [
        { label: 'Web', size: 34 },
        { label: 'iOS', size: 22 },
        { label: 'Android', size: 18 },
      ],
    },
    {
      label: 'Platform',
      children: [
        { label: 'API', size: 26 },
        { label: 'Data', size: 16 },
      ],
    },
    {
      label: 'Support',
      children: [
        { label: 'SLA', size: 12 },
        { label: 'Docs', size: 8 },
      ],
    },
  ];
}

Options

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

OptionTypeDefaultDescription
labelFieldstringlabel/size/childrenhierarchy keys
sizeFieldstringlabel/size/childrenhierarchy keys
childrenFieldstringlabel/size/childrenhierarchy keys
fillsColorValue[]palettebranch colors
sectorSpacingPixels0constant-width gap between sectors
cornerRadiusPixels0sector corner rounding
strokestylesbackground 1px with zero gapsector stroke
strokeWidthstylesbackground 1px with zero gapsector stroke
label.enabledbooleanfalsesector labels (when they fit)
label.formatter({ label, value, depth }) => stringnode namecontent
label.fontSizePixels11label font size
label.fontWeightstring | numbernormalfont weight
label.fontFamilystringtheme fontfont family
label.colorColorValueauto contrastcolor (halo in the sector color)

Tooltip and the name of a value

A node is not a row of the data — it is a name and what it adds up to — so tooltip.renderer receives NodeTooltipRendererParams: { datum?, label, value, share, color }. datum is the row the node was read from; a flow node is summed from several rows and has none.

js
tooltip: { renderer: ({ label, value, share }) => `${label}: ${value} (${Math.round(share * 100)}%)` },

Without a renderer the row of the tooltip is named after the data key the value came from — a column name, not the name of a measure. name on the series says what it should be called instead:

js
series: [{ type: 'treemap', sizeField: 'revenue', name: 'Revenue' }],