Skip to main content
Gremorie

Pie Chart

Parts of a whole, one slice per data row, with optional donut hole and token-driven colors.

Overview

PieChart is a recharts chart built on the shadcn chart primitive (ChartContainer) for parts of a whole. Pass tabular data, the nameKey (the slice label field), and the dataKey (the numeric value field). Slice colors come from each row's fill (e.g. fill: "var(--chart-1)"), and an optional config supplies the labels shown in the tooltip. Pass donut for a donut variant with a centered hole.

Reach for PieChart when proportions matter and you have two to six categories that sum to a meaningful total - traffic share by browser, revenue by tier, allocations by team. For seven or more categories, a BarChart reads faster; for time-series proportions, a stacked AreaChart carries more information.

The React edition (@gremorie/rx-data) renders with recharts inside the shadcn chart primitive; the Angular edition (@gremorie/ng-data) renders headless d3 geometry inside a styled SVG. The rendering engine differs; the public surface is identical input-for-input.

Anatomy

<pie-chart>
└─ figure (role="img")                  card surface, aspect-square
   ├─ svg
   │  ├─ slices, one per row (donut hole when donut)
   │  │  └─ row fill colored (palette / per-row fill)
   │  └─ slice labels (optional via showLabels)
   ├─ tooltip overlay (optional via tooltip)
   ├─ legend
   └─ sr-only data table

The React edition composes this surface through the shadcn chart primitive:

PieChart                          recharts pie / donut on the shadcn chart primitive
└─ ChartContainer                 responsive square frame + tooltip label config
   └─ RechartsPieChart            recharts pie surface
      ├─ ChartTooltip             hover card
      │  └─ ChartTooltipContent   themed tooltip body
      └─ Pie                      one slice per row (nameKey + dataKey); innerRadius for donut
         └─ LabelList             optional slice labels (showLabels)

Preview

'use client';import { PieChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';// Categorical (pie / radial): one chart token per row via `fill`.const browserData: ChartDatum[] = [  { browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },  { browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },  { browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },  { browser: 'Edge', visitors: 173, fill: 'var(--chart-4)' },  { browser: 'Other', visitors: 90, fill: 'var(--chart-5)' },];const browserConfig: ChartConfig = {  visitors: { label: 'Visitors' },  Chrome: { label: 'Chrome' },  Safari: { label: 'Safari' },  Firefox: { label: 'Firefox' },  Edge: { label: 'Edge' },  Other: { label: 'Other' },};export function PieChartPreview() {  return (    <PieChart      data={browserData}      config={browserConfig}      nameKey="browser"      dataKey="visitors"      donut    />  );}

Installation

bash npx gremorie@latest add rx-pie-chart
bash pnpm dlx gremorie@latest add rx-pie-chart
bash yarn dlx gremorie@latest add rx-pie-chart
bash bunx --bun gremorie@latest add rx-pie-chart

Usage

import { PieChart, type ChartConfig, type ChartDatum } from "@gremorie/rx-data";

const data: ChartDatum[] = [
{ browser: "Chrome", visitors: 275, fill: "var(--chart-1)" },
{ browser: "Safari", visitors: 200, fill: "var(--chart-2)" },
{ browser: "Firefox", visitors: 187, fill: "var(--chart-3)" },
{ browser: "Edge", visitors: 173, fill: "var(--chart-4)" },
{ browser: "Other", visitors: 90, fill: "var(--chart-5)" },
];

const config: ChartConfig = {
visitors: { label: "Visitors" },
Chrome: { label: "Chrome" },
Safari: { label: "Safari" },
Firefox: { label: "Firefox" },
Edge: { label: "Edge" },
Other: { label: "Other" },
};

export function BrowserShare() {
  return (
    <PieChart data={data} config={config} nameKey="browser" dataKey="visitors" />
  );
}
import { Component } from '@angular/core';
import { PieChart } from '@gremorie/ng-data';
import type { ChartConfig, ChartDatum } from '@gremorie/ng-data';

@Component({
selector: 'app-browser-share',
imports: [PieChart],
template: `     <pie-chart [data]="data" [config]="config" nameKey="browser" dataKey="visitors" />
  `,
})
export class BrowserShareComponent {
readonly data: ChartDatum[] = [
{ browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },
{ browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },
{ browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },
{ browser: 'Edge', visitors: 173, fill: 'var(--chart-4)' },
{ browser: 'Other', visitors: 90, fill: 'var(--chart-5)' },
];

readonly config: ChartConfig = {
visitors: { label: 'Visitors' },
Chrome: { label: 'Chrome' },
Safari: { label: 'Safari' },
Firefox: { label: 'Firefox' },
Edge: { label: 'Edge' },
Other: { label: 'Other' },
};
}

API

<PieChart>

A recharts pie/donut composed inside the shadcn chart primitive (ChartContainer). Slice colors come from each row's fill; the optional config provides the labels shown in the tooltip.

PropTypeDefaultDescription
datareadonly ChartDatum[]-Tabular data. One row per slice. Each row needs a string field at nameKey, a numeric field at dataKey, and a fill for its slice color (e.g. var(--chart-1)).
nameKeystring-Name of the slice label field. Used to resolve the tooltip label.
dataKeystring-Name of the numeric value field. Slices are sized proportionally to its sum.
configChartConfig{}Optional label/color map keyed by slice name (and value key). Powers the tooltip labels.
donutbooleanfalseWhen true, carves out a centered hole (innerRadius 60) to produce a donut chart.
showLabelsbooleanfalseWhen true, draws each slice's name inside the slice via recharts LabelList.
tooltipbooleantrueToggles the hover tooltip (ChartTooltip + ChartTooltipContent).
classNamestring-Merged onto the ChartContainer. The bundled style supplies a centered, square aspect frame.

Chart primitives for custom composition

For full control, compose raw recharts inside ChartContainer using the shadcn chart primitives, all re-exported from @gremorie/rx-data. This mirrors the official shadcn pie block (ui.shadcn.com/charts/pie).

ExportRole
ChartContainerResponsive frame; injects per-key CSS color vars (--color-<key>) from config.
ChartTooltip / ChartTooltipContentHover card and its themed content.
ChartLegend / ChartLegendContentOptional legend and its themed content.
useChartHook returning the active config from context (use inside custom children).
PieChart, Pie, Cell, LabelRaw recharts primitives composed inside ChartContainer.

Types

TypeShape
ChartDatumRecord<string, string | number> & { fill?: string } - one row of chart data; fill sets the per-slice color.
ChartConfigMap of key → { label?, icon?, color? }; provides tooltip/legend labels (and optional colors).

Slice colors come from each row's fill (e.g. fill: "var(--chart-1)"), not from config. The optional config maps each slice name to a {label} and drives the labels shown in the tooltip.

Composition

  1. Pick nameKey and dataKey - the label column and the numeric column. Both must exist on every row.
  2. Set a fill per row - point each row at a --chart-* token (e.g. fill: "var(--chart-1)"). This is where slice colors come from.
  3. Add a config mapping each slice name (and the value key) to a { label } so the tooltip reads cleanly.
  4. Order rows by magnitude before passing them in. The biggest slice should sit at the 12-o'clock anchor so the eye reads it first.
  5. Toggle donut when you want to embed a total or label at the center, or when the surrounding card is large enough that a solid pie feels too heavy.

The chart auto-sizes to its parent through ChartContainer (recharts ResponsiveContainer). The frame forces aspect-square so the slices stay circular.

Variations

Solid pie

'use client';import { PieChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';// Categorical (pie / radial): one chart token per row via `fill`.const browserData: ChartDatum[] = [  { browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },  { browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },  { browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },  { browser: 'Edge', visitors: 173, fill: 'var(--chart-4)' },  { browser: 'Other', visitors: 90, fill: 'var(--chart-5)' },];const browserConfig: ChartConfig = {  visitors: { label: 'Visitors' },  Chrome: { label: 'Chrome' },  Safari: { label: 'Safari' },  Firefox: { label: 'Firefox' },  Edge: { label: 'Edge' },  Other: { label: 'Other' },};export function PieChartPreview() {  return (    <PieChart      data={browserData}      config={browserConfig}      nameKey="browser"      dataKey="visitors"      donut    />  );}
<pie-chart [data]="data" [config]="config" nameKey="browser" dataKey="visitors" />

The default. Use when the surrounding surface is small (card, side panel) or when no centered label is needed.

Donut variant

'use client';import { PieChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';// Categorical (pie / radial): one chart token per row via `fill`.const browserData: ChartDatum[] = [  { browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },  { browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },  { browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },  { browser: 'Edge', visitors: 173, fill: 'var(--chart-4)' },  { browser: 'Other', visitors: 90, fill: 'var(--chart-5)' },];const browserConfig: ChartConfig = {  visitors: { label: 'Visitors' },  Chrome: { label: 'Chrome' },  Safari: { label: 'Safari' },  Firefox: { label: 'Firefox' },  Edge: { label: 'Edge' },  Other: { label: 'Other' },};export function PieChartDonutPreview() {  return (    <PieChart      data={browserData}      config={browserConfig}      nameKey="browser"      dataKey="visitors"      donut    />  );}
<pie-chart
  [data]="data"
  [config]="config"
  nameKey="browser"
  dataKey="visitors"
  [donut]="true"
/>

Use when you want to embed a total or label at the center of the chart, or when a solid pie feels too heavy in a large surface. The hole uses an innerRadius of 60.

Labels

Set showLabels to draw each slice's name inside the slice, so the chart reads without a separate legend.

'use client';import { PieChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';const browserData: ChartDatum[] = [  { browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },  { browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },  { browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },  { browser: 'Edge', visitors: 173, fill: 'var(--chart-4)' },  { browser: 'Other', visitors: 90, fill: 'var(--chart-5)' },];const browserConfig: ChartConfig = {  visitors: { label: 'Visitors' },  Chrome: { label: 'Chrome' },  Safari: { label: 'Safari' },  Firefox: { label: 'Firefox' },  Edge: { label: 'Edge' },  Other: { label: 'Other' },};export function PieChartLabelsPreview() {  return (    <PieChart      data={browserData}      config={browserConfig}      nameKey="browser"      dataKey="visitors"      showLabels    />  );}
<PieChart
  data={data}
  config={config}
  nameKey="browser"
  dataKey="visitors"
  showLabels
/>
<pie-chart
  [data]="data"
  [config]="config"
  nameKey="browser"
  dataKey="visitors"
  [showLabels]="true"
/>

Reach for labels when the surface is large enough to hold them and you want the slice names visible at rest. Combine with donut for a labeled donut.

Custom composition

When you need explicit per-slice colors, gradient fills, a center label, or a custom legend, compose raw recharts inside ChartContainer. This mirrors the official shadcn pie block.

import {
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
  type ChartConfig,
  type ChartDatum,
} from '@gremorie/rx-data';
import { Cell, Pie, PieChart } from 'recharts';

export function ExplicitColorPie({
  data,
  config,
  nameKey,
  dataKey,
  colorByName,
}: {
  data: ChartDatum[];
  config: ChartConfig;
  nameKey: string;
  dataKey: string;
  colorByName: Record<string, string>;
}) {
  return (
    <ChartContainer
      config={config}
      className="mx-auto aspect-square max-h-[260px]"
    >
      <PieChart>
        <ChartTooltip
          cursor={false}
          content={<ChartTooltipContent nameKey={nameKey} hideLabel />}
        />
        <Pie
          data={data}
          dataKey={dataKey}
          nameKey={nameKey}
          stroke="var(--background)"
          strokeWidth={2}
        >
          {data.map((row) => (
            <Cell
              key={String(row[nameKey])}
              fill={colorByName[String(row[nameKey])]}
            />
          ))}
        </Pie>
      </PieChart>
    </ChartContainer>
  );
}

You can also skip <Cell> entirely and set fill directly on each row of data - the wrapper does exactly that.

Accessibility

  • Accessibility layer: recharts' accessibilityLayer provides keyboard navigation and screen-reader announcements for the chart data.
  • Token contrast: --chart-1 through --chart-5 ship with WCAG AA contrast against the card background in both light and dark themes.
  • Slice separation: stroke slices with the background color (see the custom composition example) so adjacent slices stay distinguishable for users with color-vision deficiencies even when the colors are close.
  • Limit slice count: above six slices, a pie becomes hard to read. Reach for BarChart instead.
  • BarChart - reach for BarChart when you have more than six categories or want exact value comparison.
  • RadialChart - sibling polar layout when categories should be shown as concentric arcs instead of slices.
  • AreaChart - reach for a stacked AreaChart when proportions also need to vary over time.

On this page