sunburst partitions a flat hierarchy into nested angular sectors. It runs inside polar, so ring radii resolve from the final polar layout without application-owned partition rows or D3 arc generators.
import { defineChart } from '@tanstack/charts'
import { sunburst } from '@tanstack/charts/hierarchy/sunburst'
import { polar } from '@tanstack/charts/polar'
const chart = defineChart({
marks: [
polar({
startAngle: Math.PI / 2,
endAngle: Math.PI / 2 - Math.PI * 2,
marks: [
sunburst(rows, {
path: 'name',
delimiter: '.',
value: 'size',
innerRadius: ({ radius }) => radius * 0.14,
ringPadding: 2,
color: 'branchId',
stroke: '#fff',
}),
],
}),
],
})The exact @tanstack/charts/hierarchy/sunburst subpath keeps hierarchy construction and partitioning out of root, universal, ordinary polar, and radial-bar consumers.
Path input constructs parent-child relationships from a string channel:
sunburst(rows, {
path: 'name',
delimiter: '.',
value: 'size',
})Explicit parent references use nodeId because id identifies the mark:
sunburst(rows, {
id: 'package-sunburst',
nodeId: 'id',
parentId: 'parentId',
value: 'size',
})Path input may omit ancestors. Those structural nodes have data: null and empty direct lineage. Duplicate identities, invalid parents, multiple roots, and cycles throw before rendering. Source child order is preserved unless sort is supplied.
Path-mode node IDs use the shared hierarchy contract's canonical slash form, independent of the authored delimiter. The original row and path remain on data; use them when presentation must preserve source spelling. Path-mode name is the terminal path segment. Explicit-parent IDs are opaque, so their name is the complete authored ID even when it contains a slash.
SunburstPathOptions<TDatum> and SunburstParentOptions<TDatum> form the SunburstOptions<TDatum> union.
| Option | Type | Default | Meaning |
|---|---|---|---|
| path | TransformValue<TDatum, string> | Path mode only | Full hierarchy path |
| delimiter | string | / | One-character path separator |
| nodeId | TransformValue<TDatum, string> | Parent mode | Explicit node identity |
| parentId | TransformValue<TDatum, string?> | Parent mode | Explicit parent identity |
| value | TransformValue<TDatum, number?> | Required | Nonnegative contribution aggregated through parents |
| sort | SunburstNodeComparator<TDatum> | Source order | Sibling comparator over immutable node values |
| innerRadius | PolarLength | 0 | Responsive inner edge of the first rendered ring |
| outerRadius | PolarLength | Layout radius | Responsive outer edge of the last rendered ring |
| ringPadding | number | 0 | Fixed CSS-pixel gap between hierarchy depths |
| id, className | string | Derived | Stable mark identity and optional class |
| z | Channel<SunburstNode<TDatum>, ChartKey?> | No group | Geometry and interaction group |
| color | Channel<SunburstNode<TDatum>, ChartKey?> | z | Value sent to the chart color scale |
| fill, stroke | VisualChannel<SunburstNode<TDatum>, string> | Color / none | Per-sector paint |
| fillOpacity, strokeOpacity, strokeWidth | number | Renderer value | Sector presentation |
| strokeDasharray | string | None | Sector stroke dash pattern |
| opacity | number | Renderer value | Whole-sector opacity |
| motion | ChartMarkMotionOptions<SunburstNode<TDatum>>['motion'] | None | Per-node motion policy |
Nullish values contribute zero. Other values must be finite and nonnegative. ringPadding is a nonnegative pixel value. If padding consumes the available radial span, the mark omits sectors instead of emitting inverted rings.
The enclosing polar mark owns the angular sweep and final center. sunburst allocates each node's angle from its aggregate value and divides the resolved innerRadius to outerRadius span into equal depth rings. The radius options accept pixel lengths or responsive callbacks through PolarLength. Both resolved radii must be finite and nonnegative; their order controls the ring direction.
ringPadding remains a fixed pixel gap as the chart resizes. It does not change the hierarchy values or angular allocation. Sectors replay the shared renderer-neutral D3 path commands into a sampled interaction polygon, so rounded, reversed, and complete sectors retain paint-faithful focus geometry.
The root is structural and is not painted. Every rendered sector carries one SunburstNode<TDatum> with:
branchId is useful for inherited branch color: color: 'branchId' gives a top-level branch and all its descendants one color without preparing a color field. Paint, motion, and sort callbacks receive the same immutable node values.
The exact entry exports sunburst, SunburstNode, SunburstNodeComparator, SunburstPathOptions, SunburstParentOptions, and SunburstOptions.
See Polar Marks for angular sweeps and responsive PolarLength values.