Treemap
A typed treemap — nested rectangles sized by value.
Usage
import { Treemap } from '@acronis-platform/ui-react';
import type { ChartConfig } from '@acronis-platform/ui-react';Treemap is a typed composition over the shared Chart
primitives. Give it data, a per-leaf config, the size field (dataKey), and
the label field (nameKey) — it renders a themed recharts treemap (cells sized
by value, colored and labelled per name, with a tooltip and an opt-in legend), so
you don't hand-compose recharts children. There are no axes or grid.
Cell colors come from the palette prop, not from config — see
Palettes. Each config entry, keyed by a
nameKey value, maps that cell to a label and an optional tone that
re-points it within the palette. Treemap resolves them into the
--color-<name> custom properties its cells fill from.
On-cell labels adapt their text color to three fill tones: dark stops get white text, pale stops get theme-adaptive text, and inverting stops (sequential 7–8, whose fills mirror across themes) use
--ui-text-on-status-strong-primary— near-white in light, near-black in dark.
Examples
Leaves sized by value, colored and labelled per name, with a tooltip:
const data = [
{ name: 'React', size: 2400 },
{ name: 'Vue', size: 1600 },
{ name: 'Angular', size: 1200 },
{ name: 'Svelte', size: 800 },
];
const config = {
React: { label: 'React' },
Vue: { label: 'Vue' },
Angular: {
label: 'Angular'
},
Svelte: {
label: 'Svelte'
},
} satisfies ChartConfig;
<Treemap
config={config}
data={data}
dataKey="size"
nameKey="name"
className="h-[320px] w-[520px]"
/>;Change the tiling with aspectRatio, and hide the on-cell labels or tooltip with
showLabels / showTooltip:
<Treemap
config={config}
data={data}
dataKey="size"
nameKey="name"
aspectRatio={2.5}
showLabels={false}
/>Cell labels
Each cell is labelled with its leaf's name, centered in the tile by default. The
name shown is the leaf's config label, not its raw nameKey value — that
value becomes part of a --color-<name> custom property, so it has to be
CSS-safe, and a leaf whose display name contains a space is keyed by a slug with
the real name in its config entry (the same one the legend and tooltip show).
secondaryKeys adds a second line built from any other fields on the row — a
value, a count, or both — and secondaryFormatter formats each of them, receiving
the field's index in secondaryKeys so one formatter can cover fields of
different kinds:
<Treemap
config={config}
data={data} // [{ name: 'React', size: 2400, count: 24 }, …]
dataKey="size"
nameKey="name"
secondaryKeys={['size', 'count']}
secondaryFormatter={(value, index) =>
index === 0 ? `${value} kB` : `${value} files`
}
/>Labels degrade with the tile: a cell too short for two lines keeps just its name,
a line wider than its cell is truncated with an ellipsis, and a tile too small for a
label at all is left blank — which is the case the legend covers. Use labelAlign
to anchor the block at a corner instead of the default center — the corner values
are named bottom-start / top-start (not physical corners) so they mirror
under dir="rtl":
<Treemap
config={config}
data={data}
dataKey="size"
nameKey="name"
labelAlign="bottom-start"
/>Pass secondarySeparator to change what goes between the values — a space-padded
middot by default — when that reads wrong for your locale.
Legend
showLegend renders one entry per leaf — the same markers, labels and config
lookup as every other chart type — and legendPos picks its edge. It's off by
default, since a treemap labels its cells; turn it on when the small tiles are too
narrow to name themselves. The legend is laid out as a row of the chart rather than
over the tiles, and wraps onto further rows when the entries outgrow the width:
<Treemap
config={config}
data={data}
dataKey="size"
nameKey="name"
showLegend
legendPos="top"
/>Custom tooltip
Replace the tooltip with a configured ChartTooltipContent — imported from the
same library, so you never compose recharts yourself. Its formatter renders
each row and labelFormatter the header, giving you custom formatting, per-series
content, and extra fields:
import { Treemap, ChartTooltipContent } from '@acronis-platform/ui-react';
<Treemap
config={config}
data={data}
dataKey="size"
nameKey="name"
tooltipContent={
<ChartTooltipContent
nameKey="name"
hideLabel
formatter={(value, name) => `${name}: ${value.toLocaleString()}`}
/>
}
/>;Animation
Charts render statically by default. Opt in to an entrance animation with
animate, and tune it with animationDuration / animationBegin /
animationEasing:
<Treemap
config={config}
data={data}
animate
animationDuration={800}
animationEasing="ease-out"
/>animate honors prefers-reduced-motion: for a visitor who has asked their
system to reduce motion, the series render at their final geometry with no
animation (the same applies when rendering on the server).
Accessibility
Treemap sets no role and no aria-* of its own, and recharts draws a treemap
into a bare <svg> — an empty <title> / <desc> pair, no role, no tabindex. So
the plot has no accessible name and is not a tab stop, which also makes the tooltip
pointer-only.
The root is a plain <div> that spreads the props you pass, so a role and a label
land on it:
<Treemap
role="img"
aria-label="Bundle size by framework: React 2400 kB, Vue 1600 kB, Angular 1200 kB, Svelte 800 kB"
config={config}
data={data}
dataKey="size"
nameKey="name"
/>aria-labelledby works the same way, as does wrapping the chart in a <figure> with
a <figcaption>.
A name is not a text alternative, though. A treemap is a visual encoding of numbers,
so the numbers themselves have to be reachable as text — a caption, a summary
sentence, or a table beside the chart. The cell labels are real text and not glyphs
painted into an image: the cell renderer lays them out as HTML inside a
<foreignObject>, and a truncated line is a CSS ellipsis, so the full string stays in
the DOM. They carry no structural role, though, and they are conditional — a tile too
narrow or too short for one is left blank. That last case is what showLegend covers:
it renders every leaf's name as ordinary HTML in a row of its own, outside the plot.
Direction (RTL)
Under dir="rtl" the chrome around the plot — the legend row, the tooltip, and any
readouts you compose alongside them — mirrors on its own, because it is ordinary flow
layout built from logical CSS utilities. The plot area deliberately stays
left-to-right: recharts places every mark in physical SVG coordinates, so mirroring it
would flip the reading order of the data without flipping the geometry that encodes
it. ChartContainer holds that in place by pinning .recharts-surface to
direction: ltr.
The cell labels are the one thing inside the surface that is not SVG: the cell
renderer lays each one out as HTML in a <foreignObject>, aligned with logical
text-start / justify-* utilities, which is why labelAlign's corner values are
named bottom-start / top-start rather than for a physical corner. Those labels
are the deliberate exception to the pin. A <foreignObject> is a child of the
surface, so it would inherit direction: ltr along with everything else in the
plot — which would defeat the logical utilities. So ChartContainer pairs the pin
with a rule that hands the page's direction back to that subtree, and the labels
mirror. Measured in Chromium: the label block computes direction: rtl under
dir="rtl" and a bottom-start line sits against the tile's right edge, one
cell-padding in, where under dir="ltr" it sits the same distance from the
left — so the corner variants land in the tile's bottom-right in RTL. The
legend row and the tooltip
are outside the surface and mirror with the page as usual.
The two halves are worth keeping straight: the tiling does not mirror (it is geometry recharts computed in physical coordinates, like every other plot), while the labels on it do (they are HTML in flow layout). A label therefore reads from the same start edge the rest of the page reads from, on a tile that stayed put.
API Reference
Prop
Type