Treemap

A nested-rectangle treemap that visualises hierarchical sizes by area. Containers frame their children; leaf rects fill in proportional to their value. The classic squarify algorithm keeps aspect ratios as close to 1:1 as the parent will allow, so every rect stays readable even at deep nesting levels.

Customize
Layout
8px
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/treemap.json

Usage

import { Treemap } from "@craft-bits/core";
 
const nodes = [
  {
    id: "framework",
    label: "framework",
    children: [
      { id: "react", label: "react", value: 42 },
      { id: "react-dom", label: "react-dom", value: 130 },
    ],
  },
  { id: "router", label: "router", value: 22 },
];
 
<Treemap nodes={nodes} width={560} height={320} />

Pulse a region during narration:

<Treemap
  nodes={nodes}
  width={560}
  height={320}
  highlightedIds={new Set(["react-dom"])}
/>

Understanding the component

  1. Squarify layout. Children of every container are packed by the Bruls/Huijing/van Wijk squarify algorithm — the row with the worst aspect ratio is committed first, then the algorithm slices the remaining short side. The result is rectangles whose proportions stay close to 1:1 rather than the thin slivers a naive slice-and-dice layout produces.
  2. Containers and leaves coexist. A node with children is a container — its area is the sum of its descendants and its rect is drawn as a transparent outline. A node with value is a leaf and is filled. Containers carry a small inner padding so nested children sit visibly inside the parent frame.
  3. layoutId per rect. Every rect is keyed by id so when the caller swaps the nodes prop between snapshots (a before vs after a tree-shake), the rects glide between layouts via SPRINGS.smooth instead of cross-fading.
  4. Adaptive labels. Labels are pruned when the rect drops below 48 by 24 pixels. Pass hideLabels to skip them entirely (useful when the treemap is small or part of a comparison strip).
  5. Highlight by id. highlightedIds accepts a ReadonlySet of ids; matching rects render in the accent color so narration can point at a region without dropping the rest.
  6. Reduced motion. Layout transitions and rect entrances collapse to a zero-duration transition, so the final state is reachable without animation.

Props

PropTypeDefaultDescription
nodesreadonly TreemapNode[]requiredTop-level nodes. Each is a leaf (with value) or a container (with children).
widthnumber640Outer canvas width in pixels.
heightnumber360Outer canvas height in pixels.
paddingnumber8Inner padding applied inside container rects so children sit visibly inside the frame.
highlightedIdsReadonlySet<string>Set of node ids to render in the accent color.
hideLabelsbooleanfalseSuppress all labels (useful for tiny canvases).
classNamestringMerged onto the outer <div> via cn().

Accessibility

  • The root is role="img" with an aria-label summarising the number of regions, the total weight, and the five largest leaf labels. Screen-reader users hear the shape of the data without having to parse the visual encoding.
  • Rect colors are not the only signal — containers and leaves are distinguishable by border treatment and fill alpha, and highlighted regions carry a thicker accent border.
  • Interior rects carry role="presentation" so the screen reader treats the root as the single semantic image rather than reading every rect as a separate node.
  • Motion respects prefers-reduced-motion: layout transitions and rect entrances collapse to a zero-duration transition.

Credits

  • Extracted from: terminal-dreams (src/components/frontend-design/perf-bundle/ui/Treemap.tsx). The source coupled the component to a domain-specific BundleSimulator layout type with chunk frames, cache badges, and tree-shake state baked in. The library version drops every project-specific concern, generalises the input to a recursive { id, label, value, children?, color? } shape, computes the layout in-component via squarify, and keeps the original layoutId-per-rect transition story.