npx skills add ...
npx skills add ansanabria/skills --skill recharts
Build composable, responsive React charts with Recharts library. Use when creating data visualizations including line charts, area charts, bar charts, pie charts, scatter plots, and composed charts. Handles chart customization, responsive sizing, tooltips, legends, axes configuration, performance optimization, and accessibility.
This repo is now called andy-spike/skills. Both names install the same content, but the install count here only covers this one.
npx skills add ansanabria/skills --skill recharts
React charting library built on top of D3 for composable, declarative data visualization.
All Recharts charts follow the same pattern:
Recharts expects data as an array of objects. Each object represents a data point:
Use dataKey props to map object properties to chart components:
dataKey="revenue" - maps to the revenue propertydataKey={(entry) => entry.revenue - entry.expenses} - function for computed valuesCharts are built by nesting specialized components:
Sizing: Use the responsive prop (v3.3+), ResponsiveContainer wrapper, or set width/height directly
Chart types (choose one):
LineChart - Line and area visualizationsBarChart - Bar and column chartsAreaChart - Stacked and filled area chartsPieChart - Pie and donut chartsScatterChart - Scatter plots and bubble chartsComposedChart - Mixed chart typesRadarChart - Radar/spider chartsRadialBarChart - Circular bar chartsCommon child components:
XAxis / YAxis - Axis configurationCartesianGrid - Grid linesTooltip - Hover informationLegend - Series identificationLine / Bar / Area / Pie - Data series visualizationKey props:
type: "monotone" (smooth), "linear", "step", "natural"stroke: line colorstrokeWidth: line thicknessdot: point styling (set to false to hide)activeDot: hovered point stylingconnectNulls: true to connect gapsStacked areas:
Key props:
fill: bar colorradius: rounded corners [topLeft, topRight, bottomRight, bottomLeft] or single number for all cornersbarSize: fixed bar widthstackId: group bars into stacksshape: custom bar shape (function or element)Stacked bars:
Rounded stacked bars (use BarStack to round the whole stack):
Key props:
innerRadius: creates donut chart when > 0outerRadius: pie sizepaddingAngle: gap between slicesstartAngle / endAngle: partial pie (default: 0 to 360)label: shows values on slicesshape: custom render for each slice (replaces deprecated Cell component)Mix multiple chart types:
responsive prop (Recharts 3.3+, recommended)Set responsive on the chart itself. Uses standard CSS sizing rules:
Works with flexbox and CSS grid layouts. Also supports CSS style props:
ResponsiveContainer (older versions)For Recharts < 3.3, wrap chart in ResponsiveContainer:
Critical: ResponsiveContainer must have a parent with defined dimensions. Height must be a number, not a percentage.
Set width and height directly as pixels or percentages:
Control axis range:
Custom bar shape:
Chart styles:
Axis styling:
Grid styling:
The Tooltip component controls active element highlighting. Do not use activeIndex prop (removed in v3).
Tooltip interaction props:
defaultIndex: Sets initial highlighted item on renderactive: If true, tooltip remains active after interaction endstrigger: "hover" (default) or "click" for click-based interactioncontent: Custom content or () => null to hide tooltip text while keeping highlightcursor: Visual cursor in plot area, set to false to hideLink multiple charts with syncId:
Use useMemo and useCallback for props:
Separate frequently updating components:
For large datasets, consider aggregation:
Recharts includes built-in accessibility support:
Accessibility is enabled by default. The chart is keyboard navigable and screen reader compatible.
ARIA props:
Note:
Cellcomponent is deprecated and will be removed in Recharts 4.0. Use theshapeprop onBar,Pie,Scatter, etc. instead.
Components render in a default order (grid < axes < chart elements < tooltip/legend). Override with zIndex prop:
For components without direct zIndex support, wrap in ZIndexLayer:
Recharts has three coordinate systems:
x="March", y=5000). Used by ReferenceLine, ReferenceDot, ReferenceArea. Automatically converted to pixels.usePlotArea(), useOffset(), useChartWidth() hooks.getRelativeCoordinate(event, element).Converting between systems:
useXAxisScale(), useYAxisScale()useXAxisInverseScale(), useYAxisInverseScale()ResponsiveContainer has a parent with defined dimensions<XAxis angle={-45} textAnchor="end" height={80} /><XAxis interval={0} /> (0 = show all, 'preserveStartEnd' = auto)<Line isAnimationActive={false} /><Line animationDuration={500} />const data = [
{ month: 'Jan', revenue: 4000, expenses: 2400 },
{ month: 'Feb', revenue: 3000, expenses: 1398 },
];<LineChart data={data}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="value" stroke="#8884d8" strokeWidth={2} dot={{ r: 4 }} />
</LineChart><AreaChart data={data}>
<defs>
<linearGradient id="colorValue" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8}/>
<stop offset="95%" stopColor="#8884d8" stopOpacity={0}/>
</linearGradient>
</defs>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Area type="monotone" dataKey="value" stroke="#8884d8" fillOpacity={1} fill="url(#colorValue)" />
</AreaChart><Area type="monotone" dataKey="sales" stackId="1" stroke="#8884d8" fill="#8884d8" />
<Area type="monotone" dataKey="profit" stackId="1" stroke="#82ca9d" fill="#82ca9d" /><BarChart data={data}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Bar dataKey="sales" fill="#8884d8" radius={[4, 4, 0, 0]} />
<Bar dataKey="profit" fill="#82ca9d" radius={[4, 4, 0, 0]} />
</BarChart><Bar dataKey="sales" stackId="a" fill="#8884d8" />
<Bar dataKey="profit" stackId="a" fill="#82ca9d" />import { BarStack } from 'recharts';
<BarChart data={data}>
<BarStack stackId="a" radius={[4, 4, 0, 0]}>
<Bar dataKey="sales" fill="#8884d8" />
<Bar dataKey="profit" fill="#82ca9d" />
</BarStack>
</BarChart>const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042'];
<PieChart>
<Pie
data={data}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={80}
paddingAngle={5}
shape={(props) => <Sector {...props} fill={COLORS[props.index % COLORS.length]} />}
/>
<Tooltip />
<Legend />
</PieChart><ScatterChart>
<XAxis type="number" dataKey="x" name="X Axis" />
<YAxis type="number" dataKey="y" name="Y Axis" />
<CartesianGrid />
<Tooltip cursor={{ strokeDasharray: '3 3' }} />
<Scatter name="Series A" data={data} fill="#8884d8" />
</ScatterChart><ComposedChart data={data}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid stroke="#f5f5f5" />
<Tooltip />
<Legend />
<Area type="monotone" dataKey="total" fill="#8884d8" stroke="#8884d8" />
<Bar dataKey="sales" barSize={20} fill="#413ea0" />
<Line type="monotone" dataKey="profit" stroke="#ff7300" />
</ComposedChart><LineChart data={data} width="100%" height={300} responsive>
{/* chart components */}
</LineChart><LineChart data={data} responsive style={{ maxWidth: 800, width: '100%', aspectRatio: '16/9' }}>
{/* chart components */}
</LineChart><ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
{/* chart components */}
</LineChart>
</ResponsiveContainer><LineChart data={data} width={600} height={300}>
{/* chart components */}
</LineChart><XAxis
dataKey="name" // property to display
type="category" // "category" or "number"
domain={[0, 'dataMax']} // axis range
tick={{ fill: '#666' }} // tick styling
tickFormatter={(value) => `$${value}`} // format labels
angle={-45} // rotate labels
textAnchor="end" // text alignment
height={60} // extra space for labels
/>// Fixed range
<YAxis domain={[0, 100]} />
// Auto with padding
<YAxis domain={[0, 'auto']} />
// Data-based with overflow allowed
<YAxis domain={[0, 'dataMax + 100']} allowDataOverflow />
// Logarithmic scale
<YAxis type="number" scale="log" domain={['auto', 'auto']} />const CustomTooltip = ({ active, payload, label }) => {
if (active && payload && payload.length) {
return (
<div className="custom-tooltip">
<p className="label">{`${label}`}</p>
<p className="intro">{`Sales: ${payload[0].value}`}</p>
<p className="desc">Additional info...</p>
</div>
);
}
return null;
};
<Tooltip content={<CustomTooltip />} />const CustomLegend = ({ payload }) => (
<ul>
{payload.map((entry, index) => (
<li key={`item-${index}`} style={{ color: entry.color }}>
{entry.value}
</li>
))}
</ul>
);
<Legend content={<CustomLegend />} />const CustomBar = (props) => {
const { x, y, width, height, fill } = props;
return <path d={`M${x},${y} ...`} fill={fill} />;
};
<Bar shape={<CustomBar />} dataKey="sales" />
// OR
<Bar shape={(props) => <CustomBar {...props} />} dataKey="sales" /><Line
dataKey="sales"
label={{ position: 'top', fill: '#666', fontSize: 12 }}
/>
// Custom label component
<Line
dataKey="sales"
label={<CustomLabel />}
/><LineChart style={{ backgroundColor: '#f5f5f5' }}><XAxis
axisLine={{ stroke: '#666' }}
tickLine={{ stroke: '#666' }}
tick={{ fill: '#666', fontSize: 12 }}
/><CartesianGrid strokeDasharray="3 3" stroke="#e0e0e0" />{/* Click-based interaction with hidden tooltip text */}
<Tooltip trigger="click" content={() => null} cursor={false} />
{/* Default highlighted item on render */}
<Tooltip defaultIndex={2} /><LineChart onClick={(e) => console.log(e)}>
<Bar
dataKey="sales"
onClick={(data, index) => console.log('Bar clicked:', data)}
/>
</LineChart><LineChart data={data1} syncId="anyId">
{/* components */}
</LineChart>
<LineChart data={data2} syncId="anyId">
{/* components - tooltips synchronize */}
</LineChart><LineChart data={data}>
{/* other components */}
<Brush dataKey="name" height={30} stroke="#8884d8" />
</LineChart>// BAD - new function on every render
<Line dataKey={(entry) => entry.sales * 2} />
// GOOD - stable reference
const dataKey = useCallback((entry) => entry.sales * 2, []);
<Line dataKey={dataKey} />const MemoizedChart = React.memo(({ data }) => (
<LineChart data={data}>
{/* components */}
</LineChart>
));const Chart = () => {
const [hoveredData, setHoveredData] = useState(null);
return (
<LineChart>
{/* Static components */}
<Line dataKey="sales" />
{/* Dynamic overlay */}
<ReferenceLine x={hoveredData?.x} stroke="red" />
</LineChart>
);
};import { useDebouncedCallback } from 'use-debounce';
const handleMouseMove = useDebouncedCallback((e) => {
setPosition(e.activeLabel);
}, 10);
<LineChart onMouseMove={handleMouseMove}>// Bin data before rendering
const binnedData = useMemo(() => {
return d3.bin().value(d => d.x)(rawData);
}, [rawData]);<LineChart accessibilityLayer={true}>
<Line dataKey="sales" name="Monthly Sales" />
</LineChart><LineChart role="img" aria-label="Sales chart showing monthly revenue"><LineChart data={data}>
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="productA" name="Product A" stroke="#8884d8" />
<Line type="monotone" dataKey="productB" name="Product B" stroke="#82ca9d" />
<Line type="monotone" dataKey="productC" name="Product C" stroke="#ffc658" />
</LineChart><BarChart layout="vertical" data={data}>
<XAxis type="number" />
<YAxis type="category" dataKey="name" />
<Bar dataKey="value" />
</BarChart>const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042'];
<PieChart>
<Pie
data={data}
innerRadius={60}
outerRadius={80}
paddingAngle={5}
dataKey="value"
shape={(props) => <Sector {...props} fill={COLORS[props.index % COLORS.length]} />}
/>
</PieChart><LineChart data={data}>
{/* ... */}
<ReferenceLine y={5000} label="Target" stroke="red" strokeDasharray="3 3" />
<ReferenceArea x1="Jan" x2="Mar" fill="#8884d8" fillOpacity={0.1} />
</LineChart><ScatterChart>
<Scatter data={data}>
<ErrorBar dataKey="errorX" width={4} strokeWidth={2} />
<ErrorBar dataKey="errorY" width={4} strokeWidth={2} direction="y" />
</Scatter>
</ScatterChart>const SalesChart = () => {
const [timeRange, setTimeRange] = useState('month');
const data = useSelector(state => selectSalesData(state, timeRange));
return (
<ResponsiveContainer>
<LineChart data={data}>
{/* components */}
</LineChart>
</ResponsiveContainer>
);
};const RealtimeChart = () => {
const [data, setData] = useState([]);
useEffect(() => {
const interval = setInterval(() => {
setData(prev => [...prev.slice(-20), newDataPoint]);
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<LineChart data={data}>
<XAxis dataKey="time" />
<YAxis domain={['auto', 'auto']} />
<Line type="monotone" dataKey="value" isAnimationActive={false} />
</LineChart>
);
};<Line dataKey="sales" zIndex={10} /> // Render on topimport { ZIndexLayer } from 'recharts';
<ZIndexLayer zIndex={5}>
<CustomAnnotation />
</ZIndexLayer>