Skip to content

Treemap ​

Composition

"Which parts are biggest, and how much of each is already realized?" A treemap answers both at once: every tile is sized by its total, and an optional two-part split fills the solid share inside each tile - so you read magnitude (area) and progress (the split) in one glance. The classic case is export potential: tile area = total potential, the solid part = realized, the lighter part = untapped. Tiles can nest under groups, and on a narrow screen the whole thing folds into a readable single-column stack.

Example
canvas · responsive
Go deeper: Insights guide·DevTools guide

Prefer a flat list (one tile per product, each its own colour - the classic export-potential layout)? Drop the children nesting and pass leaves directly:

Example
canvas · responsive
Go deeper: Insights guide·DevTools guide

The split is generic. Name the two parts with splitLabels - ["Realized", "Untapped"], ["Used", "Free"], ["Done", "Remaining"] - nothing in the engine hardcodes a domain.

When to reach for it ​

  • Portfolio views. Hundreds of products, sectors or cost centres on one screen: area is size, the split is progress, and the whole hierarchy fits without scrolling.
  • "Where should we focus?" The big, mostly-untapped tiles are the opportunity list, no sorting required - the classic export-potential and market-scan read.
  • A dozen flat categories or fewer? A bar or Pie reads exact values faster than tile areas; the treemap earns its place at scale.

Heavy data on WebGPU Experimental ​

TreemapChart has an opt-in renderer="webgpu" that paints the tiles as GPU-instanced rectangles while labels, tooltips and the split fill stay on the SVG layer. It is capability-gated: on a browser without WebGPU it downgrades to canvas automatically, and getContext().renderer reports whichever actually painted.

⚗️ Experimental - not yet stable. WebGPU rendering is an opt-in preview. It needs a WebGPU-capable browser (Chrome / Edge, or Safari 26+); everywhere else it falls back to canvas automatically. Axes, labels and tooltips stay on the SVG layer - only the data marks are painted on the GPU.
Heavy-data demo · ~400 tiles… detecting

Play through the years ​

Tag every root-level tile with a date and flip on timeline: a year's snapshot is the root tiles sharing that date - children need no dates of their own - and the tiles tween between years as they resize. Off by default - nothing changes until a chart opts in. This is interactive year-by-year stepping, not the one-shot entrance further down.

Press the play button under the chart: it steps through the years, one snapshot at a time. Drag the scrubber to jump to any year.

tsx
const ref = useRef<TreemapChartHandle>(null);

<TreemapChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()
vue
<TreemapChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:treemapChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applyTreemapChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-treemap-chart id="c"></michi-vz-treemap-chart>
<script>
  const el = document.getElementById("c");
  el.timeline = { speedMs: 1000, loop: true };
  // el.getTimeline() -> play() / pause() / seek(year) / seekIndex(i)
</script>
  • speedMs sets the pace, loop wraps around, autoplay: true starts on mount, showControl: false hides the built-in bar.
  • Values glide between periods by default (interpolate); tune the motion with tweenMs and easing, or set interpolate: false for hard cuts. Reduced motion always gets the hard cut.
  • The headless controller is always available: chart.timeline() exposes play() / pause() / toggle() / seek(period) / seekIndex(i) / stepForward() / stepBack(), plus onStep and formatPeriod in the config for custom UI.
  • seek(period) looks for the period first, comparing as text, so seek(2021) and seek("2021") both land on 2021 whether your dates are numbers or strings. A number counts as a position (0 = first) only when no period matches, and a string that matches none does nothing. seekIndex(i) always goes by position, as the built-in scrubber does.
  • Root tiles without a date stay visible in every period.
  • timeline wins over progressiveDraw when both are set - the reveal animation further down stays off while the timeline is in control.

Reveal animation ​

The chart wipes in from left to right on mount, revealing its marks in sequence before settling into place. Off by default - a chart opts in with the progressiveDraw prop.

The marks wipe in from left to right; axes and titles stay put. With reduced motion enabled, the chart renders fully drawn instantly.

progressiveDraw: true enables the defaults (1200 ms, easeInOutCubic). A config object tunes it:

tsx
const ref = useRef<TreemapChartHandle>(null);

<TreemapChart
  ref={ref}
  {...props}
  progressiveDraw={{ durationMs: 2000 }}
/>;
// ref.current?.replay() re-runs the reveal on demand
vue
<TreemapChart :options="{ ...props, progressiveDraw: { durationMs: 2000 } }" />
svelte
<div use:treemapChart={{ ...props, progressiveDraw: { durationMs: 2000 } }}></div>
ts
applyTreemapChartProps(this.c.nativeElement, {
  ...props,
  progressiveDraw: { durationMs: 2000 },
});
html
<michi-vz-treemap-chart id="c"></michi-vz-treemap-chart>
<script>
  const el = document.getElementById("c");
  el.progressiveDraw = { durationMs: 2000 };
  // el.replay() re-runs the reveal
</script>
  • durationMs and easing ("linear", "easeOutQuad", "easeInOutCubic", or a custom (t) => t function) shape the sweep.
  • autoplay: false renders the chart fully drawn; call replay() (React ref handle, web-component method, or the core instance) to run the reveal on demand. replayOnUpdate: true re-runs it on every data change.
  • Respects prefers-reduced-motion: the chart renders fully drawn instantly.
  • Reveal animation is a one-shot entrance; play through the years above steps through data year by year instead.

Usage ​

tsx
import { TreemapChart } from "@michi-vz/react";

export default () => <TreemapChart {...props} />; // props = the chart options
vue
<script setup>
import { TreemapChart } from "@michi-vz/vue";
</script>

<template>
  <TreemapChart :options="props" />
</template>
svelte
<script>
  import { treemapChart } from "@michi-vz/svelte";
</script>

<div use:treemapChart={props}></div>
ts
// main.ts - register the elements once
import "@michi-vz/angular";
import { applyTreemapChartProps } from "@michi-vz/angular";

// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-treemap-chart #c></michi-vz-treemap-chart>
applyTreemapChartProps(this.c.nativeElement, props);
html
<script type="module" src="https://cdn.jsdelivr.net/npm/@michi-vz/wc/dist/michi-vz-wc.bundle.js"></script>

<michi-vz-treemap-chart id="c"></michi-vz-treemap-chart>
<script>
  Object.assign(document.getElementById("c"), props); // dataSet, splitLabels, …
</script>
ts
import { mountTreemapChart } from "@michi-vz/core";

const chart = mountTreemapChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();

Data shape ​

Each dataSet node is a leaf (value, optional partial) or a parent (children). A parent's value is the sum of its leaves.

ts
const props = {
  splitLabels: ["Realized", "Untapped"],
  showLegend: true,
  layout: "auto", // squarify on desktop, stack on narrow screens
  dataSet: [
    { label: "Agri-food", children: [
      { label: "Fruits", value: 100, partial: 34 },   // 34% realized
      { label: "Beverages", value: 50, partial: 35 }, // 70% realized
    ]},
    { label: "Industry", children: [
      { label: "Machinery", value: 120, partial: 64 },
    ]},
  ],
};

Responsive layout ​

layout picks the tiling algorithm: "squarify" (the treemap), "stack" (a single-column vertical partition - full-width rows, height proportional to value, with the same in-row split), or "auto" (switches to stack below stackBreakpoint, default 480px). The split, labels, tooltip, getContext() and SVG/canvas parity are identical across both layouts.

API ​

Props are typed as TreemapChartProps in @michi-vz/core. Shared across all charts: width, height, margin, colors / colorsMapping, renderer ("svg", "canvas", or experimental "webgpu"), highlightItems, disabledItems, and the on* callbacks. onChartDataProcessed / getContext() return the renderer-agnostic ChartContext. Full reference: Treemap API.