Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Sunburst chart

Budgets, disk usage and other hierarchies as nested rings you can drill into. Click a branch to zoom in, and the center or a breadcrumb takes you back out.

Charting library: TanStack Charts

Preview

Preview width

Installation

The command adds the example above to components/charts/ and, the first time, the chart kit to components/ui/charts/. Both are source files in your project from then on: read them, change them, delete what you don't use.

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

Pass one tree. Each node has a name and either a value or children, and a parent's size is the sum of its children. Node ids are the names on the path joined with " / ", so siblings need different names.

import {
  SunburstCenter,
  SunburstChart,
  SunburstHint,
  SunburstLabels,
} from "@/components/ui/charts";

const data = {
  children: [
    {
      children: [
        { name: "Salaries", value: 420 },
        { name: "Contractors", value: 95 },
      ],
      name: "People",
    },
    {
      children: [
        { name: "Cloud", value: 180 },
        { name: "Licenses", value: 60 },
      ],
      name: "Tools",
    },
    { name: "Office", value: 75 },
  ],
  name: "Spend",
};

export const SpendSunburst = () => (
  <SunburstChart ariaLabel="Spend by category" data={data} size={360}>
    <SunburstCenter />
    <SunburstLabels />
    <SunburstHint />
  </SunburstChart>
);

Anatomy

SunburstChart lays out the whole tree from data, and the other parts are optional. Without SunburstLabels the rings are bare. Without SunburstCenter, Escape or a breadcrumb is the only way back out.

<SunburstChart data={data} size={420}>
  <SunburstSegment color="var(--chart-3)" index={0} />
  <SunburstCenter />
  <SunburstLabels fontSize={12} />
  <SunburstHint />
</SunburstChart>
  • SunburstChart: Lays out the tree as rings and handles hover, zoom and the keyboard.
  • SunburstSegment: Overrides one arc by index: its color, fill or fillOpacity.
  • SunburstCenter: The disc in the middle that zooms back out a level once you've zoomed in.
  • SunburstLabels: Segment names, on the segments with room for them.
  • SunburstBreadcrumb: A slot above the chart for your own trail of crumbs.
  • SunburstHint: One line under the chart: the hovered path, or how to zoom.

Drilling down

Clicking a segment that has children zooms into it. SunburstCenter turns the middle into a disc that zooms back out a level, and Escape does the same.

Pass focusId and onFocusChange to hold the zoom in your own state. Put a trail inside <SunburstBreadcrumb> and call useSunburstBreadcrumbItems() in it for the crumbs and a zoomTo function, or derive the crumbs from focusId yourself, as this example does.

Preview width

Coloring segments

Each top-level branch takes the next chart color and its descendants share it, fading with depth. color on a node recolors it and everything under it.

For a single arc, render a <SunburstSegment> whose index is that arc's arcIndex in buildArcs(data).arcs. This example gives every arc one, coloring one branch and graying out the rest.

Preview width

Patterns

A segment's fill takes url(#id). Define a PatternLines or a gradient as a child of the chart and point a node's fill, or a SunburstSegment's, at its id.

Preview width

Driven from a legend

On hover, the segment and the ones on its path grow outward by hoverPop (8 px), and everything outside that branch fades to 25%. hoveredIndex takes an arc index, so a list beside the chart can trigger the same thing, and onHoverChange reports the chart's own hover back to it.

Here each row also sets focusId when it's clicked, zooming the rings into that department.

Preview width

Customization

The kit is in your project, in components/ui/charts/: your framework's components over a shared core/. Change a default there and every chart picks it up.

class goes on the chart's container, so size and spacing are utility classes.

SunburstHint writes one line under the chart: the hovered path, or how to zoom. Give it a function as its child to write your own. It gets the default hintText, the hoveredArc with its name, value and trail, and the current focus.

SunburstLabels takes fontSize, fill and the halo's stroke and strokeWidth (0 turns the halo off).

<SunburstHint>
  {({ hintText, hoveredArc }) => (
    hoveredArc ? (
      <span>
        {hoveredArc.name}: {hoveredArc.value.toLocaleString()}
      </span>
    ) : (
      <span>{hintText}</span>
    )
  )}
</SunburstHint>

Where a part renders content of its own, you pass it in. In Solid:

  • SunburstHint: {({ hintText, hoveredArc, focus }) => …}

Loading state

There's no loading state. A round Skeleton at the chart's size keeps the space until the tree arrives.

Animation

The rings sweep in clockwise from the top, one after another, and each label fades in once its segment is drawn. enterTransition sets the sweep (a 1.1 s tween unless you pass one), enterStaggerScale the gaps between rings, and a new playKey plays it again.

Zooming morphs the segments in place: the opened branch spreads round the full circle while everything else folds away.

Theming

Every color in the kit is one of your theme's tokens or a mix of them, except --chart-positive, a fixed green. A preset or the .dark class restyles the chart, and overriding a token in your CSS changes it everywhere.

Top-level branches take --chart-1 to --chart-5 in order, and each one's descendants share its color, fading with depth to 45% opacity.

Labels are --chart-label over a halo of --chart-background, which keeps them readable on top of the segments.

TokenDefaultPaints
--chart-1your theme'sthe first series
--chart-2your theme'sthe second series
--chart-3your theme'sthe third series
--chart-4your theme'sthe fourth series
--chart-5your theme'sthe fifth series
--chart-label--muted-foregroundtick labels
globals.css
:root {
  --chart-label: var(--foreground);
}

Accessibility

The chart is an image named by ariaLabel, "Sunburst chart of" and the root's name unless you pass one. Its default ariaDescription explains the keys below to screen reader users, so keep that sentence if you write your own.

Zooming with the keyboard keeps the focus inside the chart, so the arrow keys carry on through the new rings.

Tab
Moves focus to the chart.
Arrow Right
Moves to the next segment, down each branch before the next one. ArrowDown does the same.
Arrow Left
Moves to the previous segment. ArrowUp does the same.
Home
Jumps to the first segment.
End
Jumps to the last segment.
Enter
Zooms into the focused branch, or out a level on the center disc. Space does the same.
Escape
Zooms out one level.

API reference

Solid takes the same props as React, with class for className. A render prop is a function, called again when its arguments change, so destructured parameters stay current.

SunburstChart

PropType
dataSunburstNode

Hierarchical data tree. Parent values are summed from their children.

size?number

Chart diameter in pixels. The chart shrinks to fit narrower containers.

Default
520
playKey?number

Change this number to replay the enter animation.

Default
0
class?string

Additional class name for the chart wrapper.

focusId?string

Controlled drill-down focus: the id of the focused node (names from the root joined with " / "). Omit for uncontrolled drill-down.

onFocusChange?(focusId: string) => void

Called with the new node id when the focus changes through a segment click, the center hub, a keyboard zoom or a breadcrumb.

Type
(focusId: string) => void
hoveredIndex?number | null

Controlled hover: the index of the hovered arc in the arcs array returned by buildArcs, or null for none.

Type
number | null
onHoverChange?(index: number | null) => void

Called with the arc index (or null) when the hovered arc changes.

Type
(index: number | null) => void
hoverPop?number

How far in pixels the hovered segment grows outward.

Default
8
padding?number

Inset in pixels reserved inside the chart for hover growth, relative to size.

Default
derived from the tree depth, `size` and `hoverPop`
enterTransition?SunburstEnterTransition

Overrides the enter animation: a tween's duration (in seconds) and ease (a cubic-bezier as 4 numbers, or an easing function), or a spring.

Default
{ duration: 1.1, ease: [0.85, 0, 0.15, 1] }
enterStaggerScale?number

Scales the stagger between rings and segments during the enter animation.

Default
1
ariaLabel?string

Accessible label of the chart.

Default
"Sunburst chart of " followed by the root's `name`
ariaDescription?string

Longer accessible description of the chart.

Default
the keyboard instructions: arrow keys to inspect, Enter or Space to zoom in, Escape or the center disc to zoom out
childrenReactNode

SunburstSegment, SunburstCenter, SunburstLabels, SunburstBreadcrumb and SunburstHint children, and the patterns (PatternLines…) and gradients (LinearGradient, RadialGradient) a segment's fill references. Render one SunburstSegment per arc index to customize individual segments.

SunburstSegment

PropType
indexnumber

Arc index to customize, from the arcs array returned by buildArcs.

color?string

Color override for this segment.

fill?string

Fill override: a color, or a pattern or gradient as url(#id) (a PatternLines… or LinearGradient child of the chart, ids scoped to it). Overrides color.

fillOpacity?number

Fill opacity override (0 to 1).

Default
1 for the innermost visible ring, then fading with depth down to 0.45

SunburstCenter

PropType
class?string

Class name of the center hub's group.

SunburstLabels

PropType
fontSize?number

Label font size in pixels.

Default
11
fill?string

Label text color.

Default
"var(--chart-label)"
stroke?string

Color of the halo behind the label text.

Default
"var(--chart-background)"
strokeWidth?number

Width in pixels of the halo behind the label text. Use 0 to disable it.

Default
2.5
class?string

Class name of the group that holds the labels.

SunburstBreadcrumb

PropType
class?string

Class name of the wrapping nav element.

childrenReactNode

Breadcrumb UI, usually built from useSunburstBreadcrumbItems.

SunburstHint

PropType
class?string

Class name of the hint element.

children?ReactNode | ((context: SunburstHintContext) => ReactNode)

Custom hint content, or a function receiving the hint context. Defaults to the hovered path or zoom instructions.

Type
ReactNode | ((context: SunburstHintContext) => ReactNode)

Examples

6 examples on Solid, each live with its code and its own install command.