Category bars

Are you an AI agent?

Revenue by region, votes by option, latency by endpoint: a bar chart over names. Group a series' layers side by side, stack them or show their shares, label the values, and keep the bars readable while categories come and go.

01 — GROUP THE LAYERS
On a category axis, one BarSeries with several layers draws them side by side in each band, in layer order, colored from the theme palette. That is the default with stacking: 'off'. Two separate BarSeries still overlap.
<ChartContainer axis={{ x: { type: 'category' } }}>
<BarSeries
data={[
{ label: '2024', data: [{ x: 'Europe', value: 46 }, { x: 'Asia', value: 41 }] },
{ label: '2025', data: [{ x: 'Europe', value: 49 }, { x: 'Asia', value: 47 }] },
]}
/>
</ChartContainer>
02 — FIX THE ORDER
Bands follow the order the data first names them. axis.x.categories puts the listed names first, in list order — also a name no series has data for yet, as an empty band. Names not in the list follow in first-seen order.
<ChartContainer axis={{ x: { type: 'category', categories: ['Asia', 'Europe', 'Americas'] } }}>
03 — LABEL THE VALUES
valueLabels writes each value just past the bar's end. A label wider than its bar is skipped. The text follows <YAxis format>; pass { format } to set it apart. The color is theme.bar.labelColor.
<BarSeries data={layers} options={{ valueLabels: { format: (v) => `$${v}M` } }} />
04 — STACK, OR SHOW SHARES
stacking: 'normal' draws one column per band, with the column total as its label. 'percent' draws each layer's share of the column, so every column fills 100%; it has no labels.
<BarSeries data={layers} options={{ stacking: 'percent' }} />
05 — NEGATIVE VALUES
A negative bar grows down from zero, rounds its lower end and hangs its label below. In a 100% stack a column splits by its absolute sum: +3 and −1 give +75% up and −25% down.
<BarSeries data={[{ label: '2025', data: [{ x: 'Asia', value: 6 }, { x: 'Americas', value: -6 }] }]} />
06 — CATEGORIES THAT COME AND GO
Pass the new array. A category that leaves fades out in its own band, then the bands close up and the later bars slide over; one that comes back slides the others aside and fades in. Values that change ease to their new height. Hovering a band shades it and lists every layer in the tooltip.
const regions = away ? REGIONS.filter((r) => r !== 'Americas') : REGIONS;
 
<BarSeries data={toLayers(REVENUE, regions)} />
Grouped$M per year, Asia first
AsiaEuropeAmericasAfricaOceania
050
100% stackedshare of each region's revenue
EuropeAsiaAmericasAfricaOceania
0%100%
Negative valuespts vs. prior year
EuropeAsiaAmericasAfricaOceania
0
LiveAmericas comes and goes
EuropeAsiaAmericasAfricaOceania
050