When I build dashboards for real teams—product managers tracking feature adoption, sales leads watching pipeline mix, or ops folks monitoring incident categories—someone always asks for “a quick pie chart.” It’s deceptively simple: a circle sliced into meaningful portions. But there’s nuance. You need reliable labels, accessible colors, sensible tooltips, and interaction that doesn’t feel twitchy. A pie chart that looks good in a design mock can mislead users if you pick the wrong keys, mis-handle small slices, or forget to handle empty data.
In this walkthrough, I’ll show you exactly how I create a pie chart in React using Recharts. I’ll start with the simplest working version, then make it more production-ready: hover states, custom colors, legend behavior, responsive containers, accessibility improvements, and a strategy for dealing with long labels or tiny values. I’ll also call out when a pie chart is the wrong choice and how to make that call quickly. You’ll walk away with a runnable example and a set of practical patterns you can reuse across projects.
Why I reach for Recharts
Recharts is a React charting library built on D3 concepts but designed for React’s component model. That combination matters: I can compose a chart like any other component, keep data structures simple, and avoid the “imperative D3 in a React app” friction. I recommend Recharts when you want clean defaults, a strong API, and fast results without writing custom SVG logic.
Recharts isn’t the only option in 2026. There are excellent choices like ECharts, Chart.js, and Vega-Lite. But for teams already in React and looking for a pleasant developer experience, Recharts is still a solid choice. It handles pie charts, line charts, bar charts, composed charts, and more with a consistent API, and it plays well with TypeScript and modern React patterns.
When a pie chart is the right choice (and when it isn’t)
I use pie charts for category share at a single point in time—“percentage of total” is the right mental model. If your data answers a question like “What proportion of support tickets are billing-related?” a pie chart can be effective.
However, I skip pie charts when:
You’re comparing many categories (more than 6–8 slices)
Differences are subtle (two slices are close in size)
Users need to compare values precisely
In those cases, I recommend a bar chart. A horizontal bar chart, in particular, is usually easier to read. I still keep a pie chart for “quick glance” dashboards where the goal is “big picture” rather than precision.
Setup: React app and dependencies
If you’re starting fresh, create a React app and install Recharts. I keep this tight and standard.
Commands:
npx create-react-app foldername
cd foldername
npm i --save recharts
If you’re on a modern React setup with Vite or Next.js, Recharts still works with minimal changes. I’ll stick to the classic setup for clarity because it’s the most straightforward to follow and easy to port.
Here’s a typical dependency set that works well:
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"recharts": "^2.9.3"
}
Build the simplest pie chart first
I always start with a basic, working chart. That lets me verify data shape and make sure the chart renders. Then I layer in interaction and styling. Here’s a full example you can paste into App.js:
import React from "react";
import { PieChart, Pie, Tooltip, Cell } from "recharts";
dataKey identifies the numeric property for the slice size
Cell lets me color slices individually
Tooltip gives instant interactivity without extra code
This is enough to see a pie chart. But it isn’t ready for production: no hover, no responsive layout, no accessibility considerations, and no label handling. Let’s fix that.
Add hover interaction and polish
Hover states help users connect slice color to its meaning, especially when the labels are hidden or small. In Recharts, activeIndex and onMouseEnter make this trivial.
Here’s a version with hover and a cursor-friendly UI:
import React, { useState } from "react";
import { PieChart, Pie, Tooltip, Cell } from "recharts";
I keep activeIndex in state and let Recharts handle the highlight. The cursor style is small but important; it tells users that the chart is interactive. I also remove the outline on focus because the default outline can look odd with SVG elements. If your app has strict accessibility requirements, you’ll want a more deliberate focus treatment—more on that later.
Make it responsive the right way
Static width/height works for demos, but real dashboards need to adapt. I use ResponsiveContainer to let the chart size itself based on the parent. This prevents clipping and makes the chart usable on smaller screens.
import React, { useState } from "react";
import { ResponsiveContainer, PieChart, Pie, Tooltip, Cell } from "recharts";
outerRadius uses a percentage so it scales with container size
I set the container height explicitly; otherwise, the chart collapses. This is a common mistake—Recharts needs a height from the DOM. I set it on a parent div and let width be 100%.
Add labels without clutter
Labels on a pie chart can quickly become messy. For small slices or long names, labels overlap and become unreadable. My pattern: show labels only for slices above a certain threshold and show the rest via tooltip.
Here’s a custom label renderer:
const renderLabel = ({ name, percent }) => {
if (percent < 0.08) return null; // hide tiny slices
return ${name} (${(percent * 100).toFixed(0)}%);
};
Then I attach it:
<Pie
data={data}
dataKey="students"
outerRadius="80%"
label={renderLabel}
>
This is where a simple analogy helps: I think of labels like street signs. A few well-placed signs keep you oriented; too many make it harder to navigate. By hiding labels for very small slices, you reduce visual noise while still preserving access to details through tooltips.
If you want consistent labeling, I suggest using a custom legend instead of on-slice labels. In practice, legends are easier to scan and don’t fight for space.
Improve tooltips for clarity
Default tooltips are fine, but I often want to add a percentage or format numbers. I use a custom tooltip component for clarity and a more polished look.
const CustomTooltip = ({ active, payload }) => {
if (!active |
!payload
!payload.length) return null;
const { name, value, percent } = payload[0];
return (
<div style={{
background: "white",
border: "1px solid #ddd",
padding: "8px 12px",
borderRadius: 6,
boxShadow: "0 2px 6px rgba(0,0,0,0.1)"
}}>
{name}
{value.toLocaleString()} students
{(percent * 100).toFixed(1)}% of total
);
};
Then:
<Tooltip content={} />
This makes the chart more useful because it answers two questions at once: absolute value and share. I also format the number with toLocaleString() so large values are readable. Those little touches improve trust and reduce cognitive load.
Use colors that hold up in real UI
Colors can make or break a pie chart. I choose palettes that are distinct, accessible, and consistent across the app. If you’re not sure, a good fallback is to use a known palette or a quick generator, then manually adjust for contrast.
My rules of thumb:
Avoid pure red/green pairs for accessibility
Keep saturation consistent so no slice feels “more important” by accident
Use a neutral background to avoid color distortion
If you have more categories than colors, use modulo indexing but keep the slice ordering stable so colors don’t shuffle across renders. I also recommend keeping color assignments stable across pages, so “Revenue” is always blue, “Costs” always orange, and so on.
The production-ready example
Now I’ll combine the key pieces: responsive layout, hover, custom tooltip, label filtering, and polished styling. This is the version I’d ship in a typical dashboard.
This is complete and runnable. If you paste it into your App.js, it will render a chart that behaves nicely on most screens, reads well, and handles small slices gracefully.
Common mistakes I see (and how I avoid them)
1) Wrong dataKey: If the key doesn’t match a numeric field, the chart renders with zero values. I always log data once and check the keys.
2) Missing container height: Without a height, the chart collapses. I wrap it in a div with a fixed height or use CSS utilities.
3) Too many slices: If there are more than 8 categories, I group the smallest ones into “Other.” This reduces clutter and improves readability.
4) Color mismatch across re-renders: If the data ordering changes, colors will shift. I sort data or map colors by category name so colors stay consistent.
5) Labels everywhere: Labels make the chart unreadable for small slices. I filter or use a legend instead.
These issues are easy to miss in a quick demo but show up fast in production. I keep a small checklist for myself whenever I add a pie chart to a live dashboard.
Practical performance considerations
Pie charts are light, but there are still performance and UX considerations:
Recharts rendering usually stays snappy for 10–20 slices, typically within 10–15ms on modern laptops.
Tooltips and hover effects add minimal overhead, but re-rendering large datasets can become noticeable.
If you update data frequently (every second), consider memoizing data and chart props to avoid unnecessary re-renders.
Animations are attractive but can be distracting. I turn them off for data that updates rapidly.
If you ever profile and find the chart causing UI jank, you can memoize the chart component or reduce the update frequency. Most dashboards don’t need real-time updates for a pie chart.
Accessibility notes I actually use
SVG charts are not automatically accessible. I take a few basic steps:
Provide a text summary of the data near the chart
Ensure tooltip content is available through other means for keyboard users
Use high-contrast colors and avoid relying on color alone
Add an aria-label for the chart container
Use a legend or list that can be read by screen readers
Here’s an example of a simple text summary next to the chart:
Enrollment breakdown: Beginner React 400, Full-Stack Track 700, UI Systems 200, AI Tools 1000.
If you’re building for strict accessibility requirements, I recommend pairing the chart with a data table or list view. It doesn’t have to be huge—just enough for a screen reader to capture the data accurately.
Data shape and preprocessing patterns
The easiest way to break a pie chart is to pass in data that isn’t clean. In production, datasets often include zeros, nulls, or values you don’t want represented. I almost always add a preprocessing step, even if it’s small.
const total = safeRows.reduce((sum, r) => sum + r.value, 0);
return { safeRows, total };
};
Why this matters:
Filter out invalid values so the chart doesn’t render “ghost slices.”
Trim labels so your legend doesn’t show awkward whitespace.
Precompute totals so you can use them in tooltips or summary text.
I treat this as part of the chart component contract: if the chart is given messy input, it’s more likely to mislead users.
Handling empty and near-empty datasets
Empty data happens more often than you think: a new tenant, a filter that removes all categories, or a backend issue that returns no rows. You should explicitly handle these cases instead of rendering a blank circle.
Here’s a pattern I like:
if (!data.length) {
return (
No data available for this period.
);
}
For near-empty data—like a single category taking 98% of the total—I tend to add a threshold rule to hide labels for tiny slices and group the smallest into “Other.” That prevents the chart from being 90% label noise for 2% of the value.
Grouping small slices into “Other”
This is one of the most practical improvements for real dashboards. If you have many categories, some slices are too small to read. Grouping them into “Other” keeps the chart readable and still preserves the total.
const total = rows.reduce((sum, r) => sum + r.value, 0);
const large = [];
let otherValue = 0;
rows.forEach(r => {
if (r.value / total >= minPercent) {
large.push(r);
} else {
otherValue += r.value;
}
});
if (otherValue > 0) {
large.push({ name: "Other", value: otherValue });
}
return large;
};
I usually pick a threshold around 3–5% depending on the audience. Finance teams might want more detail, while executive dashboards favor simplicity. The key is to keep the chart legible while still respecting the data.
Make legends actually useful
Legends are often an afterthought, but they’re how many people read a pie chart. I like to customize them so they include both label and value. Recharts lets you do this with a custom legend component.
If you want the legend to include numbers, you can pass extra info by mapping your data before rendering. In many cases, I generate the legend data explicitly so the order and labels are exactly what I expect.
Maintain stable colors across sorting and filters
One of the most subtle problems with pie charts is color drift. If you sort data by value or apply filters, slice order changes, and colors shift. The result: “Marketing” might be blue on one day and orange on another, confusing users.
This is less flashy but much more trustworthy. Users learn to associate a color with a category, and they shouldn’t have to re-learn that each time.
Handling long labels and text overflow
Long category names are a reality in real products. “Payments & Billing Issues (Enterprise)” doesn’t fit nicely on a slice label. You have a few options:
Truncate labels on slices and show the full label in the tooltip.
Move labels to a legend where text can wrap.
Use a custom label renderer that adds a second line or smaller font size.
This keeps the chart clean and pushes the full label to the tooltip where there’s space to read it.
Adding a center label or total
Sometimes I want to show a total in the center of the pie chart. This works especially well when the pie chart represents the breakdown of a larger number (like total users or total revenue). Recharts can handle a custom label in the center.
const renderCenterLabel = ({ cx, cy, total }) => {
return (
{total.toLocaleString()}
);
};
Then inside the chart:
const total = data.reduce((sum, d) => sum + d.students, 0);
<Pie
data={data}
dataKey="students"
outerRadius="78%"
label={renderLabel}
>
{renderCenterLabel({ cx: 210, cy: 210, total })}
You’ll need to compute cx and cy based on your chart dimensions. Another approach is to add an overlaying
positioned over the chart center. That’s often easier and more predictable in responsive layouts.
Example: controlled animation strategy
Animations can make charts feel alive, but they can also make data feel unstable if it updates frequently. I use a simple rule: animate on first render, then disable animation for subsequent updates.
import React, { useRef } from "react";
const useAnimateOnce = () => {
const hasAnimated = useRef(false);
if (!hasAnimated.current) {
hasAnimated.current = true;
return true;
}
return false;
};
Then:
const animate = useAnimateOnce();
This keeps the initial load feeling smooth but avoids distracting animation on every data refresh.
Keyboard interaction and focus handling
Most pie chart interactions are mouse-first. If you need keyboard support, you have to explicitly add it. I do this in two steps:
1) Provide a list-based legend that users can tab through.
2) Update the active slice when a legend item is focused.
This doesn’t make the pie itself focusable, but it gives keyboard users a way to explore slices and see the tooltip content update if you sync it with activeIndex.
Troubleshooting: chart doesn’t render or looks wrong
When something breaks, I debug in a short sequence:
1) Check the data shape. Log the data array and verify dataKey matches a number.
2) Confirm container height. If you’re using ResponsiveContainer, make sure the parent has height.
3) Remove custom renderers. If the chart appears when you remove labels/tooltip, the issue is in your custom code.
4) Reduce to a minimal dataset. Try a 2-slice chart to see if the problem persists.
5) Verify CSS. Sometimes a parent container has display: none or zero height.
This sequence solves 90% of the “nothing shows” problems.
Alternative approaches: pie vs doughnut vs stacked bar
Sometimes the best solution isn’t a pie chart at all. Here’s how I decide:
Pie chart: When you have 3–6 categories and want an immediate proportion view.
Doughnut chart: When you want a center label or a bit more whitespace for labels.
Stacked bar: When you need precise comparisons and a more scalable layout.
A doughnut chart is the same as a pie chart with an inner radius. In Recharts, that’s just innerRadius on the Pie component.
I use doughnuts when I want to show a total in the center or avoid the “heavy” look of a full pie chart.
Practical scenarios where this shines
Here are a few scenarios where the patterns above have paid off for me:
Support ticket categories: Show the top 5 categories and group the rest into “Other.” The legend lists counts and percentages.
Feature adoption mix: Pie chart on a dashboard with a small footnote that the data is for the last 30 days.
Sales pipeline by stage: Useful for a quick view, but I pair it with a stacked bar for precise comparison.
Operational incident types: Small slices are hidden from labels, but all data appears in tooltip and a table below.
In each case, the trick is to use the pie chart for quick intuition and provide alternative text or tables for precise values.
Using TypeScript with Recharts
If you’re in a TypeScript codebase, you can improve the reliability of your chart with a simple type for the data:
type PieDatum = {
name: string;
students: number;
};
const data: PieDatum[] = [
{ name: "Beginner React", students: 400 },
{ name: "Full-Stack Track", students: 700 }
];
This catches common issues like students being a string from an API response. You can also add a preprocessing step that ensures numbers are converted correctly:
This makes hover interaction clearer without being flashy.
Production-ready checklist
Before I ship a pie chart, I run through this checklist:
Data: numeric values are valid and non-negative
Labels: visible only for meaningful slices
Tooltips: include both value and percentage
Colors: stable across views and accessible
Responsiveness: chart scales and doesn’t overflow
Empty state: clear message if no data
Accessibility: text summary or table available
This takes only a few minutes and saves a lot of support questions later.
Final thoughts
A pie chart in React isn’t hard, but a good pie chart takes care. Recharts gives you a strong baseline, but the final quality comes from the details: data cleaning, label management, stable color mapping, and clear tooltips. Those details matter because users will make decisions based on what they see.
If you keep the chart focused—small number of categories, stable colors, meaningful labels—it can be a powerful quick-glance tool. If the data is more complex, use a different chart and save the pie for what it does best: a clean, immediate sense of proportions.
That’s my approach. Start simple, add depth only where it improves clarity, and make sure your chart is telling the truth as well as it looks good doing it.
Expansion Strategy
Add new sections or deepen existing ones with:
Deeper code examples: More complete, real-world implementations
Edge cases: What breaks and how to handle it
Practical scenarios: When to use vs when NOT to use
Performance considerations: Before/after comparisons (use ranges, not exact numbers)
Common pitfalls: Mistakes developers make and how to avoid them
Alternative approaches: Different ways to solve the same problem
If Relevant to Topic
Modern tooling and AI-assisted workflows (for infrastructure/framework topics)
Comparison tables for Traditional vs Modern approaches
Production considerations: deployment, monitoring, scaling
Most performance problems I see in Python services aren’t “Python is slow” problems—they’re “we’re waiting on the network” problems. You call an HTTP API, the…
I still see experienced Python developers lose time to one tiny operator: is. The bug usually looks harmless: a conditional that “obviously” should be true,…
Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools,…
Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools,…