Skip to content
quartiledocsv0.1
GitHub

DonutChart

PreviewSVGPublishes selection

Shows how a total splits into a few parts, with each part’s share in the legend. Use a bar list when there are more than five or six parts.

Playground

Orders by channel
Sep 7 – Oct 6, 2026

Amount by channel, total $383,658.96: Organic search 31.2%, Paid social 30.6%, Email 23.8%, Direct 14.4%.

Orders by channel
ChannelAmountShare
Organic search$119,852.7631.2%
Paid social$117,266.2830.6%
Email$91,148.8523.8%
Direct$55,391.0714.4%
value
thickness
<DonutChart
  data={orders}
  category="channel"
  value="amount"
  select
/>

Installation

DonutChart is exported from @quartile/react, alongside every other chart and component.

git clone https://github.com/zinnoberHaus/quartile

Not on npm yet: build the package from the clone as described in Installation. The preview API may change before publication.

Usage

Pass rows and name the fields. Types, formats and axis labels come from the data's schema; see Data model.

import { DonutChart } from '@quartile/react';

// orders: { channel: 'Email', amount: 128.5 }, …
type Order = { channel: string; amount: number };

export function RevenueByChannel({ orders }: { orders: Order[] }) {
  return <DonutChart data={orders} category="channel" value="amount" select />;
}
With select, clicking a slice or a legend row (or Enter on it) toggles its category in the nearest <Selection>. See Linked selections.

Props

PropTypeDefaultDescription
datarequiredDataInput<R>—Rows to aggregate, or a dataset with a schema attached.
categoryrequiredkeyof R & string—Field whose values become the slices.
valuekeyof R & string—Measure to aggregate. Omit it to count rows per category.
aggregate'sum' | 'count' | 'mean'Aggregatevalue ? 'sum' : 'count'How rows sharing a category combine. Defaults to sum with a value, count without.
sort'desc' | 'asc' | 'none'SortOrder'desc'Slice order, clockwise from 12 o'clock. Defaults to descending.
formatFormatter—Formats values. Defaults to compact currency, integers for counts, else the field's format.
centerValueReactNode—Large figure in the center. Defaults to the formatted total.
centerLabelReactNode—Caption under the center figure. Defaults to the slice count, e.g. "4 channels".
legendbooleantrueShows the legend with each slice's share. Defaults to true.
colorsstring[] | Record<string, string>—Slice colors, by index or by category. Defaults to the categorical palette in order.
sizenumber148Diameter in px. Defaults to 148.
thicknessnumber18Ring thickness in px. Defaults to 18.
selectbooleanfalseClicking a slice or legend row (or Enter on it) toggles its category in the nearest Selection.
idstring—Publisher id for the selection. Defaults to a generated id.
selectionstring | false—Selection to read from and publish to. Defaults to the nearest; false opts out.
onSelect(predicate: Predicate | null) => void—Called after a toggle with the new predicate for category (or null).
Frame and state props shared with the charts (11) · ChartBaseProps, ChartStateProps
PropTypeDefaultDescription
heightnumber—Plot height in px. Width always fills the container.
classNamestring——
styleCSSProperties——
aria-labelstring—Accessible name. A summary of the data is generated and rendered as hidden text inside the figure.
view'chart' | 'table'—table renders the same data as a table, with the same formats, at the chart's size. Large lazy tables expose a data-view control rather than mounting every hidden row.
tooltipNotestring—Plain-text context shown with tooltips and included in the accessible chart description.
loadingboolean | string—Shows the loading state at the chart's size, so the layout never jumps.
errorboolean | string | Error | null—Shows the error state. A string or Error becomes the message.
errorCodestring—Shown in the error state; copyable for support.
onRetry() => void——
empty{ title?: string; description?: string; action?: ReactNode }—Title and description for the empty state. Shown when there are no rows.

Accessibility

Keyboard
Tab into the ring, arrow keys between slices, Home and End to jump. With select, Enter or Space toggles the focused slice; legend rows are buttons too.
Screen readers
A summary gives the total and every part with its share. The focused slice is announced as you move.
Table fallback
Screen readers always get a table of the plotted values with the same formats. view="table" shows it at the chart's size.
Report an issueBuilt Oct 21, 2026