Sunburst Chart

A composable hierarchical sunburst chart with drill-down zoom, animated segments, breadcrumb navigation, and legend sync

ProductEnterpriseNorth AmericaDirectChannelEMEAAPACProTeamsSoloStarterServicesConsultingStrategyImplementationSupportPremiumStandardPartnersReferralsAffiliatesResellersRegionalGlobalLicensing
Click a segment to zoom in · hover to inspect

Installation

pnpm dlx shadcn@latest add @bklit/sunburst-chart

Usage

The Sunburst Chart uses a composable API. Map one SunburstSegment per arc in the layout, then add optional chrome for navigation and labels:

import {
  buildArcs,
  SunburstBreadcrumb,
  SunburstCenter,
  SunburstChart,
  SunburstHint,
  SunburstLabels,
  SunburstSegment,
} from "@bklitui/ui/charts";

const data = {
  name: "Revenue",
  children: [
    {
      name: "Product",
      children: [
        { name: "Enterprise", value: 198 },
        { name: "Pro", value: 145 },
      ],
    },
    {
      name: "Services",
      children: [
        { name: "Consulting", value: 160 },
        { name: "Support", value: 90 },
      ],
    },
  ],
};

export default function RevenueSunburst() {
  const { arcs } = buildArcs(data);

  return (
    <SunburstChart data={data} size={440}>
      <SunburstBreadcrumb>
        <DrillBreadcrumb />
      </SunburstBreadcrumb>
      {arcs.map((arc) => (
        <SunburstSegment index={arc.arcIndex} key={arc.id} />
      ))}
      <SunburstCenter />
      <SunburstLabels />
      <SunburstHint />
    </SunburstChart>
  );
}

Click a segment with children to zoom in. Click the center hub or a breadcrumb crumb to zoom back out.

SunburstBreadcrumb is a chrome slot rendered above the SVG. Compose your own trail UI with useSunburstBreadcrumbItems():

import {
  SunburstBreadcrumb,
  useSunburstBreadcrumbItems,
} from "@bklitui/ui/charts";
import {
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";

function DrillBreadcrumb() {
  const { items, zoomTo } = useSunburstBreadcrumbItems();

  return (
    <BreadcrumbList>
      {items.map((item, index) => (
        <span className="contents" key={item.id}>
          {index > 0 ? <BreadcrumbSeparator /> : null}
          <BreadcrumbItem>
            {item.isCurrent ? (
              <BreadcrumbPage>{item.label}</BreadcrumbPage>
            ) : (
              <BreadcrumbLink
                render={
                  <button onClick={() => zoomTo(item.id)} type="button" />
                }
              >
                {item.label}
              </BreadcrumbLink>
            )}
          </BreadcrumbItem>
        </span>
      ))}
    </BreadcrumbList>
  );
}

Components

SunburstChart

The root component that computes the layout and provides context to children.

PropTypeDefaultDescription
dataSunburstNoderequiredHierarchical data tree
sizenumber520Chart diameter in pixels
playKeynumber0Bump to replay the enter animation
focusIdstring-Controlled drill-down focus node id
onFocusChange(focusId: string) => void-Called when focus changes via click or breadcrumb
hoveredIndexnumber | null-Controlled hover — arc index in the arcs array
onHoverChange(index: number | null) => void-Hover state callback
hoverPopnumber8Segment grow distance on hover (px), cumulative along the path from root to hovered node
paddingnumberautoInset reserved inside the view box for hover growth (derived from depth and hoverPop)
enterTransitionTransition-Override enter animation transition
enterStaggerScalenumber1Scale ring-by-ring stagger timing
classNamestring""Wrapper class

SunburstSegment

Renders one arc with enter animation, drill-down click handling, and hover grow along the ancestor path.

PropTypeDefaultDescription
indexnumberrequiredArc index from buildArcs(data).arcs
colorstringfrom data/paletteOptional color override
fillstring-Optional fill for patterns/gradients (e.g. url(#patternId))
fillOpacitynumberdepth-basedOptional fill opacity override

SunburstCenter

Navigation hub rendered after drill-down. Click to zoom out to the parent focus.

PropTypeDefaultDescription
classNamestring""Additional CSS class

SunburstBreadcrumb

Chrome slot above the chart for drill-down navigation. Pass your breadcrumb UI as children. Use useSunburstBreadcrumbItems() inside children to read the trail and zoomTo.

PropTypeDefaultDescription
classNamestring"mb-4"Nav wrapper class
childrenReactNoderequiredBreadcrumb UI composed from chart context

useSunburstBreadcrumbItems

Hook for building breadcrumb UI inside SunburstBreadcrumb:

const { items, zoomTo } = useSunburstBreadcrumbItems();
// items: { id, label, isCurrent }[]

SunburstLabels

Segment name labels positioned at arc centroids. Each label fades in after its segment finishes the enter animation.

PropTypeDefaultDescription
fontSizenumber11Label font size
fillstringvar(--chart-label)Label fill color
strokestringvar(--chart-background)Outline stroke for contrast
strokeWidthnumber2.5Outline width (0 to disable)
classNamestring-SVG group class

SunburstHint

Contextual hint text below the chart (hover trail or zoom instructions). Rendered outside the SVG.

PropTypeDefaultDescription
classNamestring"mt-3 min-h-5 text-center text-muted-foreground text-sm"Hint wrapper class
childrenReactNode | (ctx) => ReactNode-Custom hint content; receives { hintText, hoveredArc, focus }

Custom hint example:

<SunburstHint>
  {({ hintText, hoveredArc }) =>
    hoveredArc ? (
      <span className="font-medium">{hintText}</span>
    ) : (
      <span>Explore the breakdown</span>
    )
  }
</SunburstHint>

Legend

A composable legend synced to the current focus level. See the full Legend documentation for all components and options.

Data Shape

interface SunburstNode {
  name: string;
  value?: number;       // Leaf value (parent values are summed from children)
  color?: string;       // Optional color override
  fill?: string;        // Optional fill for patterns/gradients
  children?: SunburstNode[];
}

Use buildArcs(data) to get the flat arcs array, maxDepth, and focusById map for controlled drill-down and legend wiring.

See the charts gallery for pattern fills, legend sync, and drill-down interaction.

Theming

The Sunburst Chart uses CSS variables for theming. Category colors default to --chart-1 through --chart-5. Inner rings use full opacity; outer rings step down in opacity for depth:

:root {
  --chart-1: oklch(0.646 0.222 41.116);
  --chart-2: oklch(0.6 0.118 184.704);
  --chart-3: oklch(0.398 0.07 227.392);
  --chart-4: oklch(0.828 0.189 84.429);
  --chart-5: oklch(0.769 0.188 70.08);
}

Animation

  1. Enter reveal — Rings animate clockwise with staggered segment timing
  2. Label fade — Labels fade in per segment after that segment's reveal completes
  3. Drill-down zoom — Focus morphs matching arcs; others collapse to a point
  4. Hover grow — Segments on the path from root to the hovered node extend outward, capped at the thickness of one expanded ring at the first drill level; descendants shift radially to accommodate parent growth. A padded inset inside the view box (auto from hoverPop and depth) prevents clipping.
  5. Fade — Unrelated segments dim to 25% opacity when another branch is hovered (160ms ease-out)

All animations respect prefers-reduced-motion.

Dependencies

pnpm add motion

Add @visx/pattern when using PatternLines or other pattern fills.