Treemap
Free · MITSquarified treemap whose tinted tiles fade in largest first and spring to a new layout when values change, with labels only where they fit, a tooltip and arrow-key tiles.
Installation
$ npx shadcn@latest add https://goodui.dev/r/treemap.jsonThe file lands in components/goodui/treemap.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/treemap works too.
Usage
import { Treemap } from "@/components/goodui/treemap"
export function RevenueByCategory({ period }: { period: "this" | "last" }) {
const data = period === "this" ? thisMonth : lastMonth // [{ label: "Hair", value: 24800 }, …]
return <Treemap title="Revenue by category" data={data} height={240} format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }} />
}When to use
- A breakdown with more parts than a donut can hold (6–15): revenue by category, traffic by source, storage by folder.
- Comparing one period with another: switch the data and the tiles re-pack in place.
- Space-efficient part-to-whole on dashboards and phones.
- Exact comparisons of similar values: areas are hard to compare; use a ranked bar-chart.
- Up to five parts: donut-chart reads faster.
- Hierarchies with several levels: this treemap is one level.
| If you need | Use |
|---|---|
| Money breakdown | data={[{ label, value }]} format={{ style: "currency", currency: "USD" }} |
| Shares instead of values on the tiles | showShare showValues={false} |
| Compact | height={160} radius={4} gap={2} |
Examples
Traffic sources with shares
<Treemap title="Booking page traffic" data={traffic} height={160} showShare showValues={false} radius={4} />API reference
| Prop | Type | Default |
|---|---|---|
| data* | TreemapItem[] | — |
| title Chart name for screen readers and the table caption. | React.ReactNode | — |
| height Height in px. The width follows the container. | number | 240 |
| gap Surface gap between tiles in px. | number | 3 |
| radius | number | 6 |
| showValues Print each tile's value under its label when there is room. | boolean | true |
| showShare Print each tile's share instead of (or next to) the value. | boolean | false |
| format | Intl.NumberFormatOptions | — |
| locale | string | "en-US" |
| formatValue | (value: number) => string | — |
| duration Seconds the layout takes to settle in; tiles stagger by 40 ms. | number | 0.8 |
| labels Text for built-in controls and screen readers; pass translations here. | Partial<TreemapLabels> | — |
Also accepts every <div> attribute.
Accessibility
| Tab | Focuses the treemap and highlights the largest tile. |
| Arrow keys | Cycles tiles in size order. |
| Home / End | Largest or smallest tile. |
| Escape | Clears the highlight. |
- The chart is role="group" with aria-roledescription="treemap" and a summary ("Revenue by category: 7 items, total $65,100").
- A screen-reader-only table lists label, value and share for every item, including tiles too small for a printed label.
- The highlighted tile is announced as "<label>: <value>, <share>" through a polite live region and shown in a tooltip.
- Tiles are tinted (22% of the hue over the card) with a solid color bar on top, so labels stay in the foreground text color on both themes.
- Labels print only when they fit with padding; values print only when the tile is tall enough. Nothing is clipped.
Motion
- On first view tiles fade and scale in from 85% (duration 0.8 s, ease [0.16, 1, 0.3, 1]) 40 ms apart, largest first.
- When values change the tiles move and resize on a spring (stiffness 170, damping 26); labels travel with their tiles. A container resize re-packs instantly.
- Hovering or arrowing to a tile deepens its tint to 34% and outlines it.
- Reduced motion: tiles appear in place; re-packs are instant.
- Squarify runs once per render in O(n log n); each tile is a translated group with two rects, so re-packs animate eight numbers per tile.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- Items with value ≤ 0 are left out. Items are sorted by value, so order in data does not matter.
- Colors default to --chart-1 … --chart-5 by size order, then lighter tints of the same five; set color per item to pin a hue to an entity.
- The width follows the parent; height is a px prop. Labels assume roughly 6.6px per character at 12px text.
FAQ
Does it support nested groups?
No; it lays out one level. Group your data before passing it, or render one treemap per group.
Why do small tiles have no text?
A label that does not fit is not drawn (never clipped); the tooltip, live region and table carry the value.
Related
Donut or pie whose slices sweep in clockwise, re-share the ring when a legend item is toggled, push out on hover and roll the center total to the slice under the pointer.
Grouped, stacked or horizontal bars that grow from the baseline on view and morph when data changes, with a gliding hover band, legend toggles, value labels and 2px surface gaps between segments.
Calendar (GitHub-style) or matrix heatmap on one hue with cells that pop in column by column, a tooltip, arrow-key navigation that scrolls the calendar along, and a Less–More scale.