PieChart
A typed pie or donut chart for part-to-whole proportions.
Usage
import { PieChart } from '@acronis-platform/ui-react';
import type { ChartConfig } from '@acronis-platform/ui-react';PieChart is a typed composition over the shared Chart
primitives. Give it data, a per-slice config, the value field (dataKey),
and the label field (nameKey) — it renders a themed recharts pie chart (tooltip
and legend included), so you don't hand-compose recharts children.
Unlike the cartesian charts, a pie plots a single dataKey across rows and
names each slice by nameKey.
Slice colors come from the palette prop, not from config — see
Palettes. Each config entry, keyed by a slice's
nameKey value, maps that slice to a label and an optional tone that
re-points it within the palette. PieChart resolves them into the
--color-<name> custom properties its slices fill from.
Examples
A donut chart with a themed tooltip and legend:
const data = [
{ browser: 'Chrome', value: 275 },
{ browser: 'Safari', value: 200 },
{ browser: 'Firefox', value: 187 },
{ browser: 'Edge', value: 173 },
];
const config = {
Chrome: { label: 'Chrome' },
Safari: { label: 'Safari' },
Firefox: { label: 'Firefox' },
Edge: { label: 'Edge' },
} satisfies ChartConfig;
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
shape="donut"
className="h-[360px] w-[360px]"
/>;Use a filled pie with shape="pie" (the default):
<PieChart config={config} data={data} dataKey="value" nameKey="browser" />Separate the slices with paddingAngle, and tune the donut hole with
innerRadius:
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
shape="donut"
innerRadius={80}
paddingAngle={2}
/>Fill the donut hole with a headline metric via centerLabel — a value and an
optional label, centered in the hole (donut only). Compute the value from your
data, e.g. the total:
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
shape="donut"
centerLabel={{ value: '835', label: 'Visitors' }}
/>Slice geometry
startAngle / endAngle set the sweep, so the same data can read as a
semicircle or an arc instead of a full circle (0° is 3 o'clock and angles run
counter-clockwise). cornerRadius rounds each slice, and minAngle keeps a tiny
slice big enough to see and hover — at the cost of drawing it out of proportion.
A partial sweep still centres on the full plot area, so a half turn fills only half its box: give a semicircle a short, wide chart rather than a square one.
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
shape="donut"
startAngle={180}
endAngle={0}
cornerRadius={8}
paddingAngle={4}
minAngle={4}
/>Per-slice overrides
sliceSettings overrides one slice at a time, keyed by its nameKey value: its
color (in place of the config entry), whether it carries a data label
(hideLabel), and how that label reads (labelFormat). Every unlisted slice
keeps the chart-level behaviour:
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
showLabels
labelFormat="percent"
sliceSettings={{
Firefox: { color: 'var(--ui-background-status-strong-neutral)' },
Edge: { hideLabel: true },
Chrome: { labelFormat: 'name-value' },
}}
/>Custom tooltip
Replace the tooltip with a configured ChartTooltipContent — imported from the
same library, so you never compose recharts yourself. Its formatter renders
each row and labelFormatter the header, giving you custom formatting, per-series
content, and extra fields:
import { PieChart, ChartTooltipContent } from '@acronis-platform/ui-react';
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
tooltipContent={
<ChartTooltipContent
nameKey="browser"
hideLabel
formatter={(value, name) => `${name}: ${value.toLocaleString()} users`}
/>
}
/>;For the common case — a slice's value next to its share of the total — reach for
the tooltipFormat preset instead of writing a tooltipContent:
<PieChart config={config} data={data} dataKey="value" nameKey="browser" tooltipFormat="value-percent" />A tooltipContent of your own takes precedence over the preset.
Legend and margin
legendPosition moves the legend to the top edge, and margin sets the plot-area
inset (in px) — useful when outside labels or leader lines run close to the
chart's bounds:
<PieChart
config={config}
data={data}
dataKey="value"
nameKey="browser"
legendPosition="top"
margin={{ top: 16, right: 24, bottom: 16, left: 24 }}
/>Animation
Charts render statically by default. Opt in to an entrance animation with
animate, and tune it with animationDuration / animationBegin /
animationEasing:
<PieChart config={config} data={data} dataKey="visitors" nameKey="browser" animate animationDuration={800} animationEasing="ease-out" />animate honors prefers-reduced-motion: for a visitor who has asked their
system to reduce motion, the series render at their final geometry with no
animation (the same applies when rendering on the server).
Data labels
Annotate each point with its value using showLabels. labelFormatter takes the
same formatters as the axes, so a label and its axis read alike:
<PieChart config={config} data={data} dataKey="visitors" nameKey="browser" showLabels labelFormatter={formatCompactNumber} />Labels sit outside the arc by default, on the chart surface. labelPosition
takes the polar placements — outside, center, centerTop, centerBottom,
insideStart, insideEnd, end. The on-arc placements switch the label to a
token that stays legible over a saturated series colour:
<PieChart config={config} data={data} dataKey="visitors" nameKey="browser" showLabels labelPosition="insideStart" />labelFormat changes what a label reads: its value (the default), its
name-value pair, its name-percent pair, or the percent alone. A percentage
is the slice's share of the sum of every slice value, to one decimal.
labelFormatter formats the slice's value, so it reaches value and
name-value only — a share is always rendered as NN.N% (a . decimal
separator and a bare %, not locale-aware) and never passes through it:
<PieChart config={config} data={data} dataKey="visitors" nameKey="browser" showLabels labelFormat="name-percent" />Turn on labelLine to connect each slice to its label with a leader line in the
slice's own colour. Leader lines only exist outside the arc, so labelPosition
no longer applies in that mode. A label reaching out past the arc needs
horizontal room: give the chart a box wider than it is tall, or shrink the arc
with outerRadius / margin, or the outermost labels are clipped at the surface
edge.
<PieChart config={config} data={data} dataKey="visitors" nameKey="browser" showLabels labelLine labelFormat="name-percent" />Labels are off by default — with more than one series they collide easily, so turn them on for a single series or a chart with room to spare.
Accessibility
PieChart sets no role and no aria-* of its own, and the SVG the arc is drawn
into has no accessible name. That gap belongs to this component rather than
recharts: recharts takes title, desc and role as first-class chart props and
writes them straight into the plot's <svg>, but PieChart passes none of them
down — it spreads the props you give it onto its own root <div> and hands the
recharts chart only the data it computes itself, so the <title> / <desc> pair
recharts always emits stays empty. recharts' accessibility layer is on by default
and is not switched off here, so the plot is also a tab stop carrying
role="application": a focus stop that announces neither a name nor any data. The
component exposes no way to turn that off.
So put the name on the root, which spreads the props you pass. Choose the role
before copying the snippet: role="img" names the chart but also makes its subtree
presentational, and it does not remove the focusable plot inside it — wrapping the
chart in a <figure> with a <figcaption> is often the better fit here.
aria-labelledby works the same way as aria-label.
<PieChart
role="img"
aria-label="Sessions by browser: Chrome 275, Safari 200, Firefox 187, Edge 173"
config={config}
data={data}
dataKey="value"
nameKey="browser"
/>Either way, a name is not a text alternative. A pie is a visual encoding of numbers,
so the numbers themselves have to be reachable as text — a caption, a summary
sentence, or a table beside the chart. The legend and the tooltip are ordinary HTML
laid out beside the SVG rather than inside it, so the legend already names every
slice in the document; the slice labels (showLabels) and the donut's centerLabel
are real <text> rather than pixels, but they sit in the SVG with no structural
role.
Direction (RTL)
Under dir="rtl" the chrome around the plot — the legend, the tooltip, and any
readouts you compose alongside them — mirrors on its own, because it is ordinary
flow layout built from logical CSS utilities. The plot area deliberately stays
left-to-right: recharts places every mark in physical SVG coordinates, so mirroring
it would flip the reading order of the data without flipping the geometry that
encodes it. ChartContainer holds that in place by pinning .recharts-surface to
direction: ltr. The sweep from startAngle to endAngle runs the same way in
both directions either way — an angle is not a reading direction — and the
centerLabel is anchored middle, so neither depends on the pin.
The slice labels do. labelPosition="outside", the default when showLabels is on,
is the one placement recharts anchors with the direction-relative SVG keywords
text-anchor: start / end, picked per slice from which side of the centre it
falls on; the leader lines labelLine draws are placed from the same geometry.
Without the pin an inherited dir="rtl" would mirror each of those labels back
across its own slice while the leader line stayed put. The pin stops at the surface,
so the legend and the tooltip — HTML outside it — still mirror with the page.
API Reference
Prop
Type