Charting & Data Visualization
Recharts (React — recommended)
import {
LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
BarChart, Bar, PieChart, Pie, Cell,
} from 'recharts';
const data = [
{ month: 'Jan', revenue: 4000, users: 240 },
{ month: 'Feb', revenue: 3000, users: 198 },
{ month: 'Mar', revenue: 5000, users: 300 },
];
function RevenueChart() {
return (
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="revenue" stroke="#8884d8" strokeWidth={2} />
<Line type="monotone" dataKey="users" stroke="#82ca9d" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
);
}
// Bar Chart
function SalesChart() {
return (
<ResponsiveContainer width="100%" height={300}>
<BarChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Bar dataKey="revenue" fill="#8884d8" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
);
}
Chart.js (framework-agnostic)
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
// With React wrapper
import { Line, Bar, Doughnut } from 'react-chartjs-2';
function SalesChart() {
return (
<Line
data={{
labels: ['Jan', 'Feb', 'Mar'],
datasets: [{
label: 'Revenue',
data: [4000, 3000, 5000],
borderColor: '#8884d8',
tension: 0.3,
}],
}}
options={{
responsive: true,
plugins: { legend: { position: 'top' } },
scales: { y: { beginAtZero: true } },
}}
/>
);
}
D3.js (low-level, maximum control)
import * as d3 from 'd3';
function createBarChart(container: HTMLElement, data: { label: string; value: number }[]) {
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const svg = d3.select(container).append('svg')
.attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
const x = d3.scaleBand().domain(data.map((d) => d.label)).range([0, width]).padding(0.2);
const y = d3.scaleLinear().domain([0, d3.max(data, (d) => d.value)!]).range([height, 0]);
svg.append('g').attr('transform', `translate(0,${height})`).call(d3.axisBottom(x));
svg.append('g').call(d3.axisLeft(y));
svg.selectAll('rect').data(data).join('rect')
.attr('x', (d) => x(d.label)!)
.attr('y', (d) => y(d.value))
.attr('width', x.bandwidth())
.attr('height', (d) => height - y(d.value))
.attr('fill', '#8884d8')
.attr('rx', 4);
}
Library Selection
| Library |
Best For |
Learning Curve |
| Recharts |
React dashboards |
Low |
| Chart.js |
Simple charts, any framework |
Low |
| D3.js |
Custom/complex visualizations |
High |
| ECharts |
Large datasets, maps |
Medium |
| Nivo |
React, rich chart types |
Low |
Anti-Patterns
| Anti-Pattern |
Fix |
| Not using ResponsiveContainer |
Always wrap charts for responsive sizing |
| Re-rendering entire chart on data change |
Use chart update methods, not re-mount |
| Too many data points without aggregation |
Aggregate/sample data before charting |
| No loading state for async data |
Show skeleton/spinner while loading |
| Fixed pixel dimensions |
Use relative sizing (%, viewBox) |
Production Checklist
1---2name: charting3description: Chart and data visualization libraries. Recharts, Chart.js, D3.js, Victory, Nivo, ECharts. Common chart types, responsive design, and real-time updates. USE WHEN: user mentions "chart", "graph", "data visualization", "Recharts", "Chart.js", "D3", "ECharts", "Victory", "bar chart", "line chart", "dashboard" DO NOT USE FOR: PDF report generation - use `pdf-generation`; data export - use `data-export`4---5# Charting & Data Visualization67## Recharts (React — recommended)89```tsx10import {11 LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,12 BarChart, Bar, PieChart, Pie, Cell,13} from 'recharts';1415const data = [16 { month: 'Jan', revenue: 4000, users: 240 },17 { month: 'Feb', revenue: 3000, users: 198 },18 { month: 'Mar', revenue: 5000, users: 300 },19];2021function RevenueChart() {22 return (23 <ResponsiveContainer width="100%" height={300}>24 <LineChart data={data}>25 <CartesianGrid strokeDasharray="3 3" />26 <XAxis dataKey="month" />27 <YAxis />28 <Tooltip />29 <Line type="monotone" dataKey="revenue" stroke="#8884d8" strokeWidth={2} />30 <Line type="monotone" dataKey="users" stroke="#82ca9d" strokeWidth={2} />31 </LineChart>32 </ResponsiveContainer>33 );34}3536// Bar Chart37function SalesChart() {38 return (39 <ResponsiveContainer width="100%" height={300}>40 <BarChart data={data}>41 <CartesianGrid strokeDasharray="3 3" />42 <XAxis dataKey="month" />43 <YAxis />44 <Tooltip />45 <Bar dataKey="revenue" fill="#8884d8" radius={[4, 4, 0, 0]} />46 </BarChart>47 </ResponsiveContainer>48 );49}50```5152## Chart.js (framework-agnostic)5354```typescript55import { Chart, registerables } from 'chart.js';56Chart.register(...registerables);5758// With React wrapper59import { Line, Bar, Doughnut } from 'react-chartjs-2';6061function SalesChart() {62 return (63 <Line64 data={{65 labels: ['Jan', 'Feb', 'Mar'],66 datasets: [{67 label: 'Revenue',68 data: [4000, 3000, 5000],69 borderColor: '#8884d8',70 tension: 0.3,71 }],72 }}73 options={{74 responsive: true,75 plugins: { legend: { position: 'top' } },76 scales: { y: { beginAtZero: true } },77 }}78 />79 );80}81```8283## D3.js (low-level, maximum control)8485```typescript86import * as d3 from 'd3';8788function createBarChart(container: HTMLElement, data: { label: string; value: number }[]) {89 const margin = { top: 20, right: 20, bottom: 30, left: 40 };90 const width = 600 - margin.left - margin.right;91 const height = 400 - margin.top - margin.bottom;9293 const svg = d3.select(container).append('svg')94 .attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)95 .append('g').attr('transform', `translate(${margin.left},${margin.top})`);9697 const x = d3.scaleBand().domain(data.map((d) => d.label)).range([0, width]).padding(0.2);98 const y = d3.scaleLinear().domain([0, d3.max(data, (d) => d.value)!]).range([height, 0]);99100 svg.append('g').attr('transform', `translate(0,${height})`).call(d3.axisBottom(x));101 svg.append('g').call(d3.axisLeft(y));102103 svg.selectAll('rect').data(data).join('rect')104 .attr('x', (d) => x(d.label)!)105 .attr('y', (d) => y(d.value))106 .attr('width', x.bandwidth())107 .attr('height', (d) => height - y(d.value))108 .attr('fill', '#8884d8')109 .attr('rx', 4);110}111```112113## Library Selection114115| Library | Best For | Learning Curve |116|---------|----------|---------------|117| Recharts | React dashboards | Low |118| Chart.js | Simple charts, any framework | Low |119| D3.js | Custom/complex visualizations | High |120| ECharts | Large datasets, maps | Medium |121| Nivo | React, rich chart types | Low |122123## Anti-Patterns124125| Anti-Pattern | Fix |126|--------------|-----|127| Not using ResponsiveContainer | Always wrap charts for responsive sizing |128| Re-rendering entire chart on data change | Use chart update methods, not re-mount |129| Too many data points without aggregation | Aggregate/sample data before charting |130| No loading state for async data | Show skeleton/spinner while loading |131| Fixed pixel dimensions | Use relative sizing (%, viewBox) |132133## Production Checklist134135- [ ] Responsive container wrapping all charts136- [ ] Loading and empty states handled137- [ ] Accessible: color contrast, ARIA labels138- [ ] Data aggregation for large datasets139- [ ] Consistent color palette across charts140- [ ] Tooltip formatting for readability