Donut Chart
Multi-segment donut with labels. Install with npx remotion-ui@latest add donut-chart.
PrimitiveData & media
$ npx remotion-ui@latest add donut-chartA composition breakdown whose slices sweep on one after another.
import { DonutChart } from "@/remotion/primitives/donut-chart";
<DonutChart
segments={[
{ label: "Direct", value: 4820 },
{ label: "Search", value: 3140 },
{ label: "Social", value: 1960 },
]}
totalLabel="Sessions"
staggerInFrames={16}
/>Where it sits
stat-card already owns the single-value ring, so this one is strictly
multi-segment. For a filled circle rather than a ring, use pie-slice-reveal.
The centre number
showTotal counts the segments that have actually landed, not the grand total
scaled by progress. The ring and the number therefore agree on every frame — a
total that races ahead of the arcs is the usual tell of a chart animated in two
unrelated places.
Timing
Each segment sweeps over durationInFrames and starts staggerInFrames after
the one before, so the total run is stagger × (segments − 1) + duration. The
legend rows fade in with their own slice.
Pass exitAtInFrames to dismiss the chart: it fades and scales down rather than
unwinding the sweep, since a ring that retracts reads as data being withdrawn.
The centre total counts the segments that have actually landed, so ring and number agree on every frame. Each segment is a dashed circle rotated to its own start angle, which keeps every arc on one radius. `stat-card` owns the single-value ring; this one is strictly multi-segment.
Agent notes
Install first, then import the copied source component locally. AI guide →
@/remotion/primitives/donut-chart- Use when: data stories, metrics, charts, and numeric proof points.
- Customize: segments, size, thickness, colors, plus copied source for timing, layout, colors, and typography.
- Rule: do not import this component from the
remotion-uinpm package; it is copied into your project.
Usage
import { DonutChart } from "@/remotion/primitives/donut-chart";
<DonutChart
segments={[
{ label: "Direct", value: 4820 },
{ label: "Search", value: 3140 },
{ label: "Social", value: 1960 },
]}
totalLabel="Sessions"
staggerInFrames={16}
/>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| segments | ChartDatum[] | required | `{ label, value, color? }`, drawn clockwise in the order given. |
| size | number | 360 | Outer diameter in px. All type scales off it. |
| thickness | number | 46 | Ring thickness. Below a tenth of `size` it reads as a hairline. |
| colors | string[] | gold / teal / pink / indigo | Fallback colours, cycled for segments with no `color`. |
| showLegend | boolean | true | Legend rows beside the ring, arriving with their own slice. |
| showTotal | boolean | true | Running total in the hole. |
| totalLabel | string | "Total" | Caption under the total. |
| valueFormatter | (value: number) => string | compact | Formats the centre total. |
| durationInFrames | number | 26 | Length of one segment's sweep. |
| staggerInFrames | number | 9 | Frames between one segment and the next. |
| exitAtInFrames | number | undefined | Frame the chart is dismissed on. |
| frame | number | undefined | Frame override — pass the parent frame inside a `<Sequence>`. |