Skip to content

Components · Charts

DonutChart

Parts of one whole as a donut in ChartFrame: total in the middle, a legend of values and shares, and a data table.

@rookcom/monarch-react/chartsComponent

Assets by type

Assets by type

All active assets

  • Laptops 412 · 47%
  • Phones 268 · 31%
  • Monitors 131 · 15%
  • Tablets 54 · 6%
  • Accessories 9 · 1%
Assets by type
SegmentValueShare
Laptops41247%
Phones26831%
Monitors13115%
Tablets546%
Accessories91%
Total874100%

Usage examples

Folding into Other, percentages only

Loading and empty

Purpose

Shows how a total splits into a few named parts (assets by type, tickets by channel), with the numbers always available as text in the legend and the table.

Use when

  • Two to six parts of one whole, where the share matters more than exact comparison.
  • A dashboard card that needs a total and its breakdown together.

Avoid when

  • Comparing parts precisely or ranking many categories: use BarList or BarChart.
  • A single bar of proportions in a tight row: use StackedProportionBar.
  • Values that do not add up to a meaningful whole, or change over time.

Design principles

  • Built on ChartFrame: the title labels the figure, and loading, empty, error and "View as table" behave like every Monarch chart.
  • Series colours come only from the data palette in a fixed order; status colours are never slices. Pin color so a slice keeps its colour when filters change.
  • More than six slices fold the smallest into a neutral "Other"; the table still lists every segment.
  • Text carries the data: the legend shows each value and share, the plot has a generated summary, and small shares read "<1%" instead of disappearing.

API reference

import { DonutChart } from '@rookcom/monarch-react/charts';
ExportKindRenderingRoot class
DonutChartcomponentserver-safe.mn-donut-chart

Takes every ChartFrame prop except children, legend, table and height; the ref goes to the <figure>.

Exported from @rookcom/monarch-react/charts with the DonutChartSegment and DonutChartLabels types.

Generated from the TypeScript declarations of packages/react/src/components/charts/donut-chart.

Properties

Negative and non-finite values count as 0; a zero total shows the empty state.

DonutChart

A donut of parts of one whole, inside ChartFrame: palette slices, a total in the middle, a legend of values and shares, folding into "Other", and the data table ("View as table") with every segment.

PropTypeDescription
segments requiredreadonly DonutChartSegment[]
title requiredReactNodeThe chart's name. Labels the figure. Required.
actionsReactNodePeriod or metric controls in the header, such as a pill TabsFilter or a Select.
centerLabelReactNodeSmall text under `centerValue`, e.g. "assets".
centerValueReactNodeBig text in the middle. Default: the formatted total. Pass `null` for none.
classNamestring
defaultShowTablebooleanUncontrolled initial table view. Default `false`.
descriptionReactNodeOne line under the title: the period, unit or what the chart answers. Describes the figure.
emptyActionsReactNodeActions in the empty state, such as a link to set up tracking.
emptyDescriptionReactNodeEmpty-state description, such as what will appear and when.
footerReactNodeNotes under the chart: source, last updated, a footnote.
formatValueValueFormatterFormats values. Wins over `valueFormat`.
headingLevelChartFrameHeadingLevelRender the title as a heading (`h2`–`h6`) to match the page outline. Default: not a heading.
idstring
labelsDonutChartLabels
legendContent"value" | "none" | "percent" | "both"What each legend entry shows after its label. Default `both` ("1,204 · 34%").
localestringBCP 47 locale for numbers. Pass it when rendering on the server.
maxSegmentsnumberMost slices drawn. Beyond it the smallest fold into one neutral "Other" slice (the table still lists every segment). Default 6: the five palette colours plus Other.
onRetry(() => unknown)Error-state retry. Return the refetch promise to keep the button busy until it settles.
onShowTableChange((showTable: boolean) => void)
percentDigitsnumberDecimal places in shares. Default 0 (shares under 1% read "<1%").
showLegendbooleanShow the legend beside the donut. Default `true`.
showTablebooleanControlled table view.
sizenumberOuter diameter in px. Default 160.
sort"desc" | "none"`desc` (default) orders slices largest first; `none` keeps the given order.
statusChartFrameStatus`ready` (default) shows the chart; `loading` a skeleton with `aria-busy`; `empty` an empty state; `error` an error with Try again when `onRetry` is set. Exposed as `data-status`.
summarystringReplaces the generated text summary (the donut's accessible name).
tableTogglebooleanShow the "View as table" toggle. Default `true` when `table` is set.
thicknessnumberRing thickness in px. Default 22% of `size`.
valueFormatIntl.NumberFormatOptions`Intl.NumberFormat` options for values.

Variants

PropValuesDescription
legendContent'both''value''percent''none'What each legend entry shows after its label.
sort'desc''none'Largest slice first (default), or the given order.
size / thickness'number (px)'Outer diameter (default 160) and ring width (default 22% of size).
status'ready''loading''empty''error'ChartFrame states, exposed as data-status on the figure.

States

StateExposed asBehaviour
Readydata-status="ready"Donut, centre and legend.
Loadingdata-status="loading"ChartFrame skeleton with aria-busy and "Loading chart".
Emptydata-status="empty"Automatically when the total is 0, or set explicitly.
Errordata-status="error"ChartFrame error, with Try again when onRetry is set.
Table viewdata-view="table"The visible data table replaces the donut.

Interaction behaviour

  • Hovering a slice dims the others and shows its label, value and share as a native tooltip.
  • "View as table" swaps the donut for the data table and back.

Keyboard support

KeysAction
Tab, then Enter or SpaceToggles "View as table".

Slices are not focusable; their values are in the legend, the summary and the table.

Accessibility

Role
figure, with the donut as role="img"
Accessible name
The figure is labelled by title; the donut by the generated summary ("Total 864. Laptops 412 (48%), …") or summary.
  • The SVG and the centre text are hidden; the legend list and the data table are real text.
  • The data table is always in the page for screen readers, and visible through the toggle.
  • Slices are separated by gaps, so neighbours stay distinct in forced-colours mode, where every slice is drawn in CanvasText.
  • Every word (Other, Segment, Value, Share, Total, ChartFrame labels) is replaceable through labels.

Responsive behaviour

  • The legend sits beside the donut and wraps below it on narrow screens; the donut never shrinks below size unless the container is narrower.

Theme support

  • Slices use --mn-color-data-1 … -5, contrast-checked against every chart background in light, dark and high-contrast themes; "Other" uses --mn-color-text-subtle.
  • The empty ring is --mn-color-bg-muted; the frame uses ChartFrame tokens.

Do and don't

Do

  • Keep to a handful of parts; let maxSegments fold the long tail into Other.
  • Pin color per entity when the same parts appear in several charts.
  • Use centerLabel to name the unit of the total ("assets", "tickets").

Don't

  • Do not use status colours for slices, even for statuses: label them instead.
  • Do not use a donut for values that do not sum to a whole.
  • Do not hide the legend unless the same breakdown is listed next to it.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsChartFrame

Testing coverage

  • Figure named by the title; generated summary; centre total; legend with value and share; hidden table with a total row.
  • One palette arc per non-zero segment, largest first; sort none and pinned colours.
  • Folding into a neutral Other with every segment kept in the table.
  • View-as-table toggle with the keyboard and back.
  • Custom centre, formatter, legend content and summary; hiding centre and legend.
  • Empty, loading and error states; tiny shares keep a sliver and read "<1%"; ref; SSR.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/donut-chart; Cross-browser axe: /components/donut-chart

Tests: packages/react/src/components/charts/donut-chart