Stacked

Stacked charts pile series on top of each other, to show how parts add up to a total or share it.

LLMs

Code

Stacked charts are drawn with TanStack Charts, through the kit's Chart components. They paint the chart with the theme's colors, grid and type, and add three palette props. The chart itself is a TanStack definition, written the way TanStack documents it.

Install

npx antuikit@latest init
npx antuikit@latest add tanstack-chart
The first command installs AntuikitProvider and the theme tokens. The second adds the chart components and TanStack Charts itself.

Your first stacked chart

Describe the chart with defineChart, outside the component, and pass it to Chart. Set color on an areaY to a category field: rows that share an x stack on their own, and the top edge traces the total.
import { areaY, colorLegend, colorLegendItems, defineChart, ruleY } from "@tanstack/charts"
import { scaleLinear } from "@tanstack/charts/scales/linear"
import { scalePoint } from "@tanstack/charts/scales/point"
import { tooltip } from "@tanstack/charts/tooltip"
import { Chart, chartColors, seriesTooltip } from "@/components/ui/tanstack-chart"
const unemployed = [
{ month: "Jan", industry: "Retail", unemployed: 1240 },
{ month: "Jan", industry: "Manufacturing", unemployed: 980 },
{ month: "Feb", industry: "Retail", unemployed: 1310 },
{ month: "Feb", industry: "Manufacturing", unemployed: 1020 },
{ month: "Mar", industry: "Retail", unemployed: 1190 },
{ month: "Mar", industry: "Manufacturing", unemployed: 940 },
]
const definition = defineChart(
{
marks: [
areaY(unemployed, {
x: "month",
y: "unemployed",
color: "industry",
fillOpacity: 0.78,
}),
ruleY([0], { stroke: chartColors.axis }),
],
scales: {
x: { scale: scalePoint },
y: { scale: scaleLinear, grid: true, axis: { label: "Unemployed (thousands)" } },
},
color: {
range: [...chartColors.series],
legend: colorLegend({ label: "Industry", items: colorLegendItems({ gap: 16 }) }),
},
},
{ focus: "group-x", tooltip: { use: tooltip, content: seriesTooltip } },
)
export function UnemploymentChart() {
return (
<Chart
definition={definition}
ariaLabel="Unemployed workers by industry, stacked"
height={300}
/>
)
}
The chart fills its container's width, so give it a height. ariaLabel is required.

Which component

All three take the same definition and the same palette props.
Chart components

Bars and areas

areaY, barY and barX all stack the same way. Name the series with z, or with color alone, and rows that share a position stack. The bars are covered in full on the Bar page.
barY(causes, {
x: "month",
y: "deaths",
z: "cause",
color: "cause",
key: (row) => `${row.month}:${row.cause}`,
})
  • Setting y1 or y2 (x1 or x2 on barX) turns stacking off and keeps the edges you gave.
  • The tooltip shows each row's own value, not the height of its stack.

Order

Series stack in the order they first appear in the rows. Pass layout: stack({ order }) to choose: a list names the series from the bottom up, and "ascending" or "descending" sorts them by total.
import { areaY, stack } from "@tanstack/charts"
areaY(revenue, {
x: "month",
y: "revenue",
z: "channel",
color: "channel",
layout: stack({ order: ["In store", "Online"] }),
})
// The color, legend and tooltip order, separate from the stack order.
color: { domain: ["Online", "In store"] }
The stack order and the color order are separate. Set color: { domain } to fix which series gets which color, and pass sort: "color-domain" to the tooltip to list its rows in the same order.

Normalize to 100%

offset: "normalize" stretches every position to the full height, so each layer shows its share. The value scale then runs from 0 to 1: give it that domain and format the ticks as percentages.
import { format } from "d3-format"
import { scaleLinear } from "d3-scale"
areaY(rows, {
x: "date",
y: "unemployed",
color: "industry",
layout: stack({ offset: "normalize" }),
})
scales: {
y: {
scale: scaleLinear().domain([0, 1]),
grid: true,
axis: { ticks: { format: format(".0%") }, label: "Share" },
},
}

Diverging stacks

The default offset, "diverging", stacks positive values up from zero and negative values down from it. Negate one group, and the two sides grow away from a shared zero line.
For an ordered scale, such as survey answers, keep the counts positive and pass anchor. It shifts each bar so a point inside one series sits on zero, here the middle of the neutral answers.
const responses = ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"]
barX(counts, {
x: "count",
y: "Question",
z: "Response",
color: "Response",
layout: stack({
order: responses,
anchor: { series: "Neutral", fraction: 0.5 },
}),
})
// Show distances from zero as positive numbers.
x: {
scale: scaleLinear,
axis: { ticks: { format: (value: number) => `${Math.abs(value)}` } },
}
anchor works with the diverging offset only.

Streamgraph

offset: "wiggle" with order: "inside-out" floats the layers around a moving center, balancing the layers above and below it. Use it for values of zero or more, and for the overall shape rather than exact values.
areaY(rows, {
x: "date",
y: "unemployed",
z: "industry",
color: "industry",
layout: stack({ offset: "wiggle", order: "inside-out" }),
})

Colors

The chart takes the theme's chart colors without any setup. Change the palette on the chart itself, or paint one mark with fill and chartColors. The Color guideline covers which palette fits which data.
<Chart definition={definition} ariaLabel="..." palette="multi" />
// An ordered scale in your own colors, from one end to the other.
color: {
domain: ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"],
range: [
"var(--ant-red-6)",
"var(--ant-red-3)",
"var(--ant-color-fill-secondary)",
"var(--ant-blue-3)",
"var(--ant-blue-6)",
],
}
Palette props
Taken by Chart, RendererChart and CanvasChart. Rows marked antuikit in the Version column are added by the kit.

Animation

Use RendererChart with TanStack's motion() renderer to animate marks in and between data updates. It skips the animation when the reader has asked for reduced motion.
import { motion } from "@tanstack/charts/motion"
import { RendererChart } from "@/components/ui/tanstack-chart"
const renderer = motion({ initial: "always" })
<RendererChart definition={definition} renderer={renderer} ariaLabel="..." />

API

The options a stacked chart uses most. TanStack Charts' documentation has the full reference.
Stacking options of areaY, barY and barX
stack