How to Create a Treemap Chart Using D3.js in Angular

Angular D3 Treemap Chart Tutorial

Visualizing hierarchical data can be challenging when you need to display large amounts of information in a limited space. A treemap chart represents hierarchical data using nested rectangles. Each rectangle represents a data item, and its size is proportional to a numerical value.

Treemaps are useful for financial portfolios, storage usage, sales distribution, analytics dashboards, and resource allocation. In this tutorial, we’ll create a responsive treemap chart in Angular using D3.js.

What is a Treemap Chart?

A treemap displays hierarchical data as nested rectangles. The area of each rectangle corresponds to the value associated with that item, while colors help distinguish groups.

1. Create an Angular Project

If you already have an Angular application, use your existing project. Otherwise, create a new one:

ng new angular-d3-treemap
cd angular-d3-treemap

Install D3.js and its TypeScript definitions:

npm install d3
npm install -D @types/d3

2. Define the Hierarchical Data

D3 treemaps work with nested data. Parent nodes contain children, and leaf nodes contain numerical values.

export interface TreemapNode {
name: string;
value?: number;
children?: TreemapNode[];
}
export const TREEMAP_DATA: TreemapNode = {
name: 'Company',
children: [
{
name: 'Engineering',
children: [
{ name: 'Frontend', value: 40 },
{ name: 'Backend', value: 60 },
{ name: 'Infrastructure', value: 30 }
]
},
{
name: 'Marketing',
children: [
{ name: 'Digital', value: 35 },
{ name: 'Content', value: 20 },
{ name: 'Events', value: 15 }
]
},
{
name: 'Sales',
children: [
{ name: 'Enterprise', value: 50 },
{ name: 'SMB', value: 25 }
]
}
]
};

Here, Company is the root node, departments are parent nodes, and teams are leaf nodes with numerical values.

3. Create the Angular Treemap Component

Generate a component:

ng generate component treemap-chart

In treemap-chart.component.ts, use D3 to calculate and render the chart. The component uses ResizeObserver to redraw the chart when its container changes size.

import {
AfterViewInit,
Component,
ElementRef,
OnDestroy,
ViewChild
} from '@angular/core';
import * as d3 from 'd3';
interface TreemapNode {
name: string;
value?: number;
children?: TreemapNode[];
}
@Component({
selector: 'app-treemap-chart',
standalone: true,
templateUrl: './treemap-chart.component.html',
styleUrl: './treemap-chart.component.css'
})
export class TreemapChartComponent implements AfterViewInit, OnDestroy {
@ViewChild('chartContainer', { static: true })
chartContainer!: ElementRef;
private resizeObserver?: ResizeObserver;
private readonly data: TreemapNode = {
name: 'Company',
children: [
{ name: 'Engineering', children: [
{ name: 'Frontend', value: 40 },
{ name: 'Backend', value: 60 },
{ name: 'Infrastructure', value: 30 }
]},
{ name: 'Marketing', children: [
{ name: 'Digital', value: 35 },
{ name: 'Content', value: 20 },
{ name: 'Events', value: 15 }
]},
{ name: 'Sales', children: [
{ name: 'Enterprise', value: 50 },
{ name: 'SMB', value: 25 }
]}
]
};
ngAfterViewInit(): void {
this.renderTreemap();
this.resizeObserver = new ResizeObserver(() => this.renderTreemap());
this.resizeObserver.observe(this.chartContainer.nativeElement);
}
private renderTreemap(): void {
const container = this.chartContainer.nativeElement;
const width = container.clientWidth;
const height = 420;
if (width <= 0) return;
const host = d3.select(container);
host.selectAll('svg').remove();
const root = d3.hierarchy(this.data)
.sum(node => node.value ?? 0)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
const layout = d3.treemap()
.size([width, height])
.paddingOuter(4)
.paddingInner(3)
.paddingTop(22)
.round(true)(root);
const color = d3.scaleOrdinal()
.domain(['Engineering', 'Marketing', 'Sales'])
.range(['#4e79a7', '#f28e2b', '#59a14f']);
const svg = host.append('svg')
.attr('viewBox', `0 0 ${width} ${height}`)
.attr('width', '100%')
.attr('height', height)
.attr('role', 'img')
.attr('aria-label', 'Treemap showing company budget allocation by department');
const departments = svg
.selectAll<SVGGElement, d3.HierarchyRectangularNode>('g.department')
.data(layout.children ?? [])
.join('g')
.attr('class', 'department');
departments.append('rect')
.attr('x', d => d.x0)
.attr('y', d => d.y0)
.attr('width', d => Math.max(0, d.x1 - d.x0))
.attr('height', d => Math.max(0, d.y1 - d.y0))
.attr('fill', d => color(d.data.name))
.attr('rx', 4);
departments.append('text')
.attr('x', d => d.x0 + 8)
.attr('y', d => d.y0 + 16)
.attr('fill', '#fff')
.attr('font-size', 12)
.attr('font-weight', '600')
.text(d => d.data.name);
const teams = departments
.selectAll<SVGGElement, d3.HierarchyRectangularNode>('g.team')
.data(d => d.leaves())
.join('g')
.attr('class', 'team');
teams.append('rect')
.attr('x', d => d.x0)
.attr('y', d => d.y0)
.attr('width', d => Math.max(0, d.x1 - d.x0))
.attr('height', d => Math.max(0, d.y1 - d.y0))
.attr('fill', d => color(d.parent?.data.name ?? ''))
.attr('fill-opacity', 0.88)
.attr('stroke', '#fff')
.attr('stroke-width', 1)
.attr('rx', 2);
teams.append('text')
.attr('x', d => d.x0 + 6)
.attr('y', d => d.y0 + 17)
.attr('fill', '#fff')
.attr('font-size', 11)
.attr('pointer-events', 'none')
.text(d => d.data.name)
.each(function(d) {
const label = d3.select(this);
if (d.x1 - d.x0 - 12 < 45 || d.y1 - d.y0 13) {
label.text(d.data.name.slice(0, 12) + '…');
}
});
teams.append('text')
.attr('x', d => d.x0 + 6)
.attr('y', d => d.y0 + 33)
.attr('fill', '#fff')
.attr('font-size', 10)
.attr('pointer-events', 'none')
.text(d => d.data.value ?? 0)
.each(function(d) {
if (d.y1 - d.y0 < 40) d3.select(this).remove();
});
}
ngOnDestroy(): void {
this.resizeObserver?.disconnect();
}
}

How it works

  • d3.hierarchy() converts the nested object into a hierarchy.
  • .sum() calculates aggregated values for parent nodes.
  • d3.treemap() computes rectangle coordinates: x0, y0, x1, and y1.
  • An ordinal color scale assigns consistent colors to departments and their teams.
  • ResizeObserver redraws the chart when the container changes size and is disconnected when the component is destroyed.

4. Create the Component Template

In treemap-chart.component.html:

<div class="treemap-wrapper">
<h2>Company Budget Allocation</h2>
<p class="chart-description">
Explore the relative budget allocation across departments and teams.
</p>
<div #chartContainer class="treemap-container"></div>
</div>

5. Add Component Styles

In treemap-chart.component.css:

:host {
display: block;
width: 100%;
}
.treemap-wrapper {
width: 100%;
padding: 16px;
box-sizing: border-box;
font-family: Arial, sans-serif;
}
.treemap-wrapper h2 {
margin: 0 0 8px;
font-size: 22px;
color: #222;
}
.chart-description {
margin: 0 0 16px;
color: #666;
font-size: 14px;
line-height: 1.5;
}
.treemap-container {
width: 100%;
min-height: 200px;
overflow: hidden;
}
.treemap-container svg {
display: block;
max-width: 100%;
}
.treemap-container rect {
transition: fill-opacity 150ms ease;
}
.treemap-container .team:hover rect {
fill-opacity: 1;
stroke: #222;
stroke-width: 1.5;
}

6. Use the Component in Your Angular Application

For a standalone Angular application, import the chart component into your root component. For example, in app.component.ts:

import { Component } from '@angular/core';
import { TreemapChartComponent } from './treemap-chart/treemap-chart.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [TreemapChartComponent],
template: '<main><app-treemap-chart></app-treemap-chart></main>'
})
export class AppComponent {}

Adjust the import path to match your project, then start the development server:

ng serve

Open the local URL shown by Angular CLI, usually http://localhost:4200.

7. Customize the Treemap

Change the chart height

const height = 500;

You can also calculate height from the container width to maintain an aspect ratio.

Change the color palette

const color = d3.scaleOrdinal()
.domain(['Engineering', 'Marketing', 'Sales'])
.range(['#6a51a3', '#238b45', '#cb181d']);

Choose colors with enough contrast to keep labels readable.

Adjust rectangle spacing

d3.treemap()
.size([width, height])
.paddingOuter(6)
.paddingInner(4)
.paddingTop(26);
  • paddingOuter() controls spacing around the overall layout.
  • paddingInner() controls spacing between sibling rectangles.
  • paddingTop() reserves room for parent labels.

Try a different tiling strategy

d3.treemap()
.tile(d3.treemapBinary)
.size([width, height]);

D3 also provides d3.treemapSquarify, d3.treemapResquarify, and d3.treemapSliceDice. Each strategy arranges the rectangles differently.

8. Common Issues and Troubleshooting

IssueWhat to check
Chart is not displayedEnsure the view has initialized and the container width is greater than zero.
TypeScript errors for coordinatesUse the rectangular hierarchy returned by d3.treemap().
Labels overlapReduce font sizes or hide labels in smaller rectangles.
Incorrect rectangle sizesCheck that leaf values are valid, non-negative numbers.
Chart does not resizeVerify that ResizeObserver observes the correct container.
Repeated SVG elementsRemove the previous SVG before redrawing.

For production applications, handle empty data and zero-value datasets, validate incoming API data, and provide an accessible text alternative to the visualization.

9. When Should You Use a Treemap?

  • Financial dashboards: Show portfolio allocation by sector and asset.
  • Storage monitoring: Display disk usage by folder and file category.
  • Sales analytics: Compare revenue across regions, products, and teams.
  • Business intelligence: Visualize budgets, expenses, and resource distribution.
  • Application monitoring: Compare resource consumption across services.

A treemap is less suitable when readers need to compare very similar values precisely. A bar chart may make those differences easier to judge.

Conclusion

In this tutorial, we created a treemap chart in Angular using D3.js. We covered hierarchical data, layout calculation, SVG rendering, group-based colors, labels, and responsive resizing.

Angular and D3.js work well together when you need more control over a visualization than a predefined chart component offers. You can extend this example with tooltips, click events, API-driven data, drill-down navigation, and configurable color schemes.

Published by Kumar Gandhi K

Hi! I’m Kumar and I live in Bangalore (IN) with my family. By profession I’m a Web Developer experienced in a vast variety of frameworks and technologies, like, HTML, CSS, JavaScript, Angular, Bootstrap… Using these i have built or sometimes maintained mid market and enterprise level applications. I worked for few software companies over the years and on few domains like finance, field-service and storage. I also did consulting job at one point. I am loyal and hard (or for better word smart) working individual. In my free time I read books, in fact I buy a lot of books hoping that some day I might find all the time in the world to read them and I also enjoy watching TV.