Acronis UIKit
Components

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

Edit on GitHub

On this page