Home/ Cookbook/ Charts/ Stacked bars

Show parts-of-whole per bucket with a stacked bar chart

By the end of this recipe you'll have stacked vertical bars — one bucket per category, the bar height is the total, the segments are the components.

Charts Intermediate ~10 min React · @corelithzw/react

Overview

One bar per bucket. Each bar is split into segments that sum to the total. The bottom segment is the only one with a true baseline.

Stacked bars are the discrete cousin of stacked areas. Use them for "what mix produced this total in each period?" — revenue by channel per month, hours by task per project, inventory by status per branch. The shared baseline lets the reader compare totals; the segments tell the mix story underneath.

Skip stacked bars if you actually want to compare the middle segments — they don't share a baseline so the eye can't read them precisely. Use grouped bars for that, or a multi-line if the x-axis is ordered.

Default opinion: only the bottom segment is readable for value. Put your most-important segment at the bottom of the stack.

The chart

Monthly revenue by channel — POS, online, wholesale — for the last six months.

Revenue by channel, last six months Six stacked bars. POS holds the steady base; online grows month-on-month; wholesale appears in month four. $120k$90k$60k$30k Jan Feb Mar Apr May Jun
POS Online Wholesale
Totals: Jan $38k, Feb $44k, Mar $52k, Apr $68k, May $86k, Jun $106k. POS grows steadily; online overtakes POS by Jun; wholesale starts Apr and ramps.

Required pieces

From @corelithzw/react.

Intended exports used here: Chart.Bar with stack.

Roadmap. Stacked mode ships with Chart.Bar in @corelithzw/react v0.2.

The React snippet

Same shape as grouped bars; flip group to stack.

RevenueByChannel.tsx@corelithzw/react

Customising

Three forks.

Percent-stacked

If the question is "what's the share?" instead of "what's the total?", normalise to 100% per bar.

<Chart.Bar
  data={data}
  stack="percent"
  series={mySeries}
  yFormat={(v) => `${Math.round(v * 100)}%`}
  height={280}
/>

Show segment labels

Print the value inside each segment when there's room. Auto-hide on the segments smaller than ~28px tall.

<Chart.Bar
  data={data}
  stack
  segmentLabels="auto"
  height={300}
/>

Diverging stack

Put positive segments above the axis and negative ones below — perfect for "added vs removed inventory".

<Chart.Bar
  data={data}
  stack="diverging"
  series={[
    { key: 'added',   color: 'success' },
    { key: 'removed', color: 'danger', sign: -1 },
  ]}
/>

In context

Inside a monthly review card.

Revenue mix · last 6 months
$394k total

Accessibility

Three colours sharing one bar is the rough patch — labels and order do the lifting.

  • Role and label. Root <svg> uses role="img" + aria-labelledby.
  • Segment-level labels. Each <rect> sits in a <g aria-label="Jun · POS: $40k">; the screen reader can walk the chart segment by segment.
  • SR summary. A .ck-sr-only paragraph names the total trajectory and which segment dominates.
  • Order matters. The legend lists segments bottom-to-top so it matches the chart's visual order — bottom in the stack is first in the legend.
  • Keyboard. Arrow left/right walks the buckets; Arrow up/down walks the stack.