KQ-1319 · TREE/MAP
Tree Map
A spending treemap you zoom into with a real relayout. Every node has a rectangle in every view — squarified fresh for the plot's shape inside the focus, and the root's layout seen through the camera outside it — so a zoom glides each tile on glide from where it was to where it goes: the chosen category opens out to fill the plot while its children re-squarify into the new shape, siblings fly off past the edges and fade on the exit ease, and grandchildren grow out of their parent's old box; breadcrumbs reverse it. Labels are decided from each tile's final size and fade as they come and go, and a readout names the tile under the pointer. The tiles are buttons with one roving tab stop: arrows move to the nearest tile in that direction, Enter zooms in or selects a leaf, Backspace and Escape zoom out, and focus lands on the tile you came out of.
Fernworks spend
$1,082,900 · Q3 2026
Fernworks spend7 items
Fernworks spend · $1,082,900 · 100.0% of spend · 7 items
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/tree-map
Props
- depth
- number
- default 2
- How many levels the view draws below the node in focus, 1 to 3: 1 is flat tiles, 2 and 3 frame each tile with a header and nest its children inside.
- labels
- "full" | "name" | "off"
- default "full"
- Tile labels: name, amount and share where they fit (fewer lines where they do not, none on slivers); the name alone; or none.
- palette
- "category" | "size" | "change"
- default "category"
- How tiles are coloured: a hue per top-level category shaded by rank, one hue by share of the focus, or warm for spend that grew and green for spend that shrank.
- data
- TreeMapNode
- default defaultTreeMapData
- The tree: id, name, children, and on leaves a value and an optional previous value. A parent's amount is the sum of its children; color overrides the palette for a branch.
- value / defaultValue
- string
- default the root's id
- The id of the node the view is zoomed into. Controlled or uncontrolled; an id that is not a parent falls back to the root.
- onValueChange
- (id: string) => void
- default —
- Fires from the tile, crumb or key that zoomed, with the new focus id.
- onSelect
- (node: TreeMapNode, path: TreeMapNode[]) => void
- default —
- A leaf was pressed or chosen with Enter, with the path from the root down to it.
- format
- (value: number) => string
- default —
- How an amount reads in tiles, the readout, the table and spoken names. Defaults to whole currency in locale.
- locale
- string
- default "en-US"
- The locale for amounts and shares.
- currency
- string
- default "USD"
- The currency for amounts.
- period
- string
- default "Q3 2026"
- The period the amounts cover, shown beside the total.
- compareLabel
- string
- default "last quarter"
- What the change is measured against, in the readout and spoken names.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the numbers have arrived: loading shows a skeleton of blocks, error a retry.
- onRetry
- () => void
- default —
- Try again was pressed after the numbers failed to load.
- title
- string
- default "Spend"
- The heading.
- label
- string
- default —
- The accessible name. Defaults to the title.
- sound
- boolean
- default false
- Play a swish on every zoom (higher in, lower out) and a click for a leaf, a crumb or a keyboard move.
- disabled
- boolean
- default false
- Shows the spend but refuses zooms and selections.
- className
- string
- default —
- Classes for the outer panel.
| Prop | Type | Default | Description |
|---|---|---|---|
| depth | number | 2 | How many levels the view draws below the node in focus, 1 to 3: 1 is flat tiles, 2 and 3 frame each tile with a header and nest its children inside. |
| labels | "full" | "name" | "off" | "full" | Tile labels: name, amount and share where they fit (fewer lines where they do not, none on slivers); the name alone; or none. |
| palette | "category" | "size" | "change" | "category" | How tiles are coloured: a hue per top-level category shaded by rank, one hue by share of the focus, or warm for spend that grew and green for spend that shrank. |
| data | TreeMapNode | defaultTreeMapData | The tree: id, name, children, and on leaves a value and an optional previous value. A parent's amount is the sum of its children; color overrides the palette for a branch. |
| value / defaultValue | string | the root's id | The id of the node the view is zoomed into. Controlled or uncontrolled; an id that is not a parent falls back to the root. |
| onValueChange | (id: string) => void | — | Fires from the tile, crumb or key that zoomed, with the new focus id. |
| onSelect | (node: TreeMapNode, path: TreeMapNode[]) => void | — | A leaf was pressed or chosen with Enter, with the path from the root down to it. |
| format | (value: number) => string | — | How an amount reads in tiles, the readout, the table and spoken names. Defaults to whole currency in locale. |
| locale | string | "en-US" | The locale for amounts and shares. |
| currency | string | "USD" | The currency for amounts. |
| period | string | "Q3 2026" | The period the amounts cover, shown beside the total. |
| compareLabel | string | "last quarter" | What the change is measured against, in the readout and spoken names. |
| status | "ready" | "loading" | "error" | "ready" | Whether the numbers have arrived: loading shows a skeleton of blocks, error a retry. |
| onRetry | () => void | — | Try again was pressed after the numbers failed to load. |
| title | string | "Spend" | The heading. |
| label | string | — | The accessible name. Defaults to the title. |
| sound | boolean | false | Play a swish on every zoom (higher in, lower out) and a click for a leaf, a crumb or a keyboard move. |
| disabled | boolean | false | Shows the spend but refuses zooms and selections. |
| className | string | — | Classes for the outer panel. |