Charting Libraries for React Dashboards
Dashboards without charts are just tables. Charts convey trends, patterns, and outliers in seconds—data that would take minutes to extract from rows and columns. Recharts, built specifically for React in 2015, dominates the SaaS ecosystem, used by 68% of React dashboards in 2026 according to npm download trends.
In my experience, Recharts wins for three reasons: (1) it's composable React components, not a wrapper around D3, (2) animations and responsive sizing are built-in, and (3) the learning curve is gentle. This article teaches you to build three chart types and connect them to live data.
What You'll Learn
You'll create:
- A responsive line chart showing revenue over time
- A bar chart comparing metrics across time periods
- A pie chart of customer distribution by plan
- Real-time chart updates when data changes
- Tooltips and legends that explain the data
Prerequisites
You need the data-fetching setup from the previous article. Install Recharts:
npm install recharts
Understanding of SVG coordinates and color palettes is helpful but not required.
Step 1: Build a Revenue Line Chart
A line chart is perfect for time-series data: revenue over months, user growth, API calls per day. Create src/components/RevenueChart.tsx:
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
import { useQuery } from '@tanstack/react-query';
interface RevenueData {
month: string;
revenue: number;
target: number;
}
export function RevenueChart() {
const { data, isLoading } = useQuery<RevenueData[]>({
queryKey: ['revenue'],
queryFn: async () => {
const response = await fetch('/api/revenue');
if (!response.ok) throw new Error('Failed to fetch revenue');
return response.json();
},
});
if (isLoading) return <p>Loading chart...</p>;
if (!data) return <p>No data available</p>;
return (
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Revenue Trend</h2>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip formatter={(value) => `$${value.toLocaleString()}`} />
<Legend />
<Line
type="monotone"
dataKey="revenue"
stroke="#3b82f6"
strokeWidth={2}
dot={{ fill: '#3b82f6', r: 5 }}
activeDot={{ r: 7 }}
name="Actual Revenue"
/>
<Line
type="monotone"
dataKey="target"
stroke="#9ca3af"
strokeWidth={2}
strokeDasharray="5 5"
name="Target"
/>
</LineChart>
</ResponsiveContainer>
</div>
);
}
Key components:
ResponsiveContainermakes the chart adapt to its parent width.LineChartwraps all other elements.Linedefines a data series; you can add multiple lines.Tooltipshows values on hover.formatterinTooltipcustomizes how values display (e.g., currency).
Step 2: Build a Bar Chart for Comparisons
Bar charts are ideal for comparing values across categories. Create src/components/ConversionChart.tsx:
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
import { useQuery } from '@tanstack/react-query';
interface ConversionData {
name: string;
free: number;
pro: number;
enterprise: number;
}
export function ConversionChart() {
const { data, isLoading } = useQuery<ConversionData[]>({
queryKey: ['conversions'],
queryFn: async () => {
const response = await fetch('/api/conversions');
if (!response.ok) throw new Error('Failed to fetch conversions');
return response.json();
},
});
if (isLoading) return <p>Loading chart...</p>;
if (!data) return <p>No data available</p>;
return (
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Conversions by Plan</h2>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="free" fill="#ef4444" name="Free Plan" />
<Bar dataKey="pro" fill="#3b82f6" name="Pro Plan" />
<Bar dataKey="enterprise" fill="#10b981" name="Enterprise" />
</BarChart>
</ResponsiveContainer>
</div>
);
}
Each Bar is a separate series. Colors distinguish plans. Grouped bars (the default) make comparison easy.
Step 3: Build a Pie Chart for Distribution
Pie charts show parts of a whole. Create src/components/CustomerDistributionChart.tsx:
import {
PieChart,
Pie,
Cell,
Legend,
Tooltip,
ResponsiveContainer,
} from 'recharts';
import { useQuery } from '@tanstack/react-query';
interface PlanData {
name: string;
value: number;
}
const COLORS = ['#ef4444', '#3b82f6', '#10b981', '#f59e0b'];
export function CustomerDistributionChart() {
const { data, isLoading } = useQuery<PlanData[]>({
queryKey: ['customers-by-plan'],
queryFn: async () => {
const response = await fetch('/api/customers-by-plan');
if (!response.ok) throw new Error('Failed to fetch customer distribution');
return response.json();
},
});
if (isLoading) return <p>Loading chart...</p>;
if (!data) return <p>No data available</p>;
return (
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Customers by Plan</h2>
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
labelLine={false}
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
outerRadius={80}
fill="#8884d8"
dataKey="value"
>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip formatter={(value) => `${value} customers`} />
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
);
}
The label prop shows percentages on slices. Cell applies colors cyclically. A pie chart works best when you have 3–5 categories.
Step 4: Combine Charts in a Dashboard
Create src/pages/AnalyticsDashboard.tsx:
import { RevenueChart } from '../components/RevenueChart';
import { ConversionChart } from '../components/ConversionChart';
import { CustomerDistributionChart } from '../components/CustomerDistributionChart';
export default function AnalyticsDashboard() {
return (
<div className="space-y-6 p-6">
<h1 className="text-3xl font-bold text-gray-900">Analytics</h1>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<RevenueChart />
<ConversionChart />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<CustomerDistributionChart />
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Key Metrics</h2>
<ul className="space-y-2 text-gray-700">
<li>Total Revenue: <span className="font-bold">$142,450</span></li>
<li>Avg. Monthly Growth: <span className="font-bold">12%</span></li>
<li>Customer Lifetime Value: <span className="font-bold">$3,240</span></li>
<li>Churn Rate: <span className="font-bold">0.89%</span></li>
</ul>
</div>
</div>
</div>
);
}
This layout stacks charts responsively. On desktop, two charts per row; on mobile, one per row (via grid-cols-1 lg:grid-cols-2).
Charting Library Comparison
| Library | Learning Curve | Bundle Size | Customization | Best For |
|---|---|---|---|---|
| Recharts | Low | 35 KB | High (React props) | Most React dashboards |
| Chart.js | Medium | 50 KB | Medium (config objects) | Simple charts, canvas-based |
| Plotly | Medium | 120 KB | High | Interactive scientific charts |
| D3 | Steep | 100 KB | Extreme | Custom bespoke visualizations |
Recharts dominates React dashboards because its API is React-native: components and props instead of imperative method calls.
Key Takeaways
- Recharts is a React-first charting library, not a wrapper around D3 or Canvas, so it feels natural to React developers.
ResponsiveContainerhandles sizing—charts scale to their parent width automatically.- Tooltips and legends aid understanding without cluttering the chart itself.
- Color consistency across charts (e.g., same color for "Pro" plan everywhere) improves dashboard coherence.
- Pie charts work for 3–5 categories; use bar charts for comparisons across many categories.
Frequently Asked Questions
How do I update charts in real-time?
Charts re-render when their data prop changes. If you're using TanStack Query with refetchInterval, the chart updates automatically when new data arrives. For WebSocket updates, set state on message arrival and charts re-render instantly.
Can I customize chart colors?
Yes. In Recharts, use the stroke prop for line color, fill prop for bar/pie color, or colors array for multiple series. Or use the Cell component to color individual segments (as in the pie chart example).
Why is my chart not rendering?
Check (1) that data is not null/undefined, (2) that ResponsiveContainer has a parent with a defined height, and (3) that the chart type matches your data (e.g., don't use PieChart for time-series data).
How do I export a chart as an image?
Recharts renders to SVG (not Canvas), so right-click "Save image as..." works. For programmatic export, use a library like html2canvas or recharts-to-png to convert the SVG to PNG.
Should I use stacked bars or grouped bars?
Grouped bars are easier to compare across series. Stacked bars show totals and parts. Use stacked for "breakdown" questions (Where does revenue come from?); grouped for "comparison" questions (Which plan converts most?).