Developing Treemap Diagrams for Hierarchy Visualization

Imagine you have a company budget dashboard with 500+ expense items. You spend 20 minutes hunting for the 'hole.' Our treemap shows it in 2 seconds. A treemap does one thing well: shows the proportion of parts in a hierarchical structure. The area of each rectangle is proportional to a value, color

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1422
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1288
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    984
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    1000

Imagine you have a company budget dashboard with 500+ expense items. You spend 20 minutes hunting for the 'hole.' Our treemap shows it in 2 seconds. A treemap does one thing well: shows the proportion of parts in a hierarchical structure. The area of each rectangle is proportional to a value, color encodes an additional dimension — growth/decline, category, status. If you need to understand what takes the most space in a budget, category tree, file system, or asset portfolio, a treemap is read faster than any table. Development on React 18 and D3.js v7 gives full control over each pixel: custom tooltip, animation on drill-down, responsive layout for any resolution. Even with 10,000 nodes, the layout builds in 30–50 milliseconds — sufficient for real-time updates. Starting from $1,500 for a basic treemap, this solution typically saves 30% of analysis time.

What problems does a treemap solve?

Poor readability of tables with hierarchy. When a table has 10,000 rows, finding the top 5 categories requires scrolling and sorting. A treemap makes them visually apparent — the brain processes area instantly.

Difficulty perceiving proportions. Tables have numbers, but to understand how much the marketing budget outweighs the development budget, you need to do division. A treemap gives the answer without calculation.

Need to compare shares in real time. When data updates (e.g., stock quotes), a treemap with color-encoded changes shows which assets are rising and which are falling — at a glance.

How we implement a treemap with React and D3.js

We use React 18 and D3.js v7. D3 is not a chart library but a Swiss Army knife for working with data and SVG. It gives full control over every rectangle. We don't use high-level wrappers because each project requires custom solutions: custom tooltip, drill-down animation, responsive layout. The core of our hierarchy visualization is a custom React component that integrates seamlessly with your data.

Here is a typical treemap component implementation:

import { useEffect, useRef } from 'react'; import * as d3 from 'd3'; interface TreeNode { name: string; value?: number; children?: TreeNode[]; change?: number; // % change for color encoding } interface TreemapProps { data: TreeNode; width?: number; height?: number; colorBy?: 'category' | 'change'; } export function Treemap({ data, width = 800, height = 500, colorBy = 'category' }: TreemapProps) { const svgRef = useRef<SVGSVGElement>(null); useEffect(() => { if (!svgRef.current) return; const svg = d3.select(svgRef.current); svg.selectAll('*').remove(); // Hierarchy const root = d3.hierarchy(data) .sum(d => d.value ?? 0) .sort((a, b) => (b.value ?? 0) - (a.value ?? 0)); // Layout d3.treemap() .size([width, height]) .paddingOuter(3) .paddingInner(2) .paddingTop(18) .round(true)(root); const colorScale = colorBy === 'change' ? d3.scaleDiverging(d3.interpolateRdYlGn).domain([-30, 0, 30]) : d3.scaleOrdinal(d3.schemeTableau10); const tooltip = d3.select('body').append('div') .style('position', 'absolute') .style('display', 'none') .style('background', 'rgba(15,23,42,0.9)') .style('color', '#f1f5f9') .style('padding', '8px 12px') .style('border-radius', '4px') .style('font-size', '13px') .style('pointer-events', 'none') .style('max-width', '220px'); // All nodes with children (for groups) const leaves = root.leaves(); const ancestors = root.descendants().filter(d => d.depth === 1); // Background rectangles for groups svg.selectAll('.group-rect') .data(ancestors) .join('rect') .attr('class', 'group-rect') .attr('x', (d: any) => d.x0) .attr('y', (d: any) => d.y0) .attr('width', (d: any) => d.x1 - d.x0) .attr('height', (d: any) => d.y1 - d.y0) .attr('fill', (d: any) => colorBy === 'change' ? '#e2e8f0' : d3.color(colorScale(d.data.name))!.brighter(0.7).toString()) .attr('stroke', '#fff') .attr('stroke-width', 2); // Group labels svg.selectAll('.group-label') .data(ancestors) .join('text') .attr('class', 'group-label') .attr('x', (d: any) => d.x0 + 6) .attr('y', (d: any) => d.y0 + 13) .attr('font-size', 11) .attr('font-weight', '600') .attr('fill', '#374151') .text((d: any) => d.data.name); // Leaves const cell = svg.selectAll('.cell') .data(leaves) .join('g') .attr('class', 'cell') .attr('transform', (d: any) => `translate(${d.x0},${d.y0})`); cell.append('rect') .attr('width', (d: any) => d.x1 - d.x0) .attr('height', (d: any) => d.y1 - d.y0) .attr('fill', (d: any) => { if (colorBy === 'change') return colorScale(d.data.change ?? 0); return colorScale((d.parent?.data.name ?? '') as string); }) .attr('fill-opacity', 0.85) .attr('stroke', '#fff') .attr('stroke-width', 1) .on('mouseover', (event, d: any) => { d3.select(event.currentTarget).attr('fill-opacity', 1); const pct = d.data.change != null ? `<br/>Change: ${d.data.change > 0 ? '+' : ''}${d.data.change.toFixed(1)}%` : ''; tooltip .style('display', 'block') .style('left', `${event.pageX + 12}px`) .style('top', `${event.pageY - 28}px`) .html(`<strong>${d.data.name}</strong><br/>${d3.format(',.0f')(d.value ?? 0)}${pct}`); }) .on('mouseout', (event) => { d3.select(event.currentTarget).attr('fill-opacity', 0.85); tooltip.style('display', 'none'); }); // Text inside cells (only if enough space) cell.append('text') .attr('x', 4) .attr('y', 14) .attr('font-size', 11) .attr('fill', '#fff') .attr('font-weight', '500') .text((d: any) => { const w = d.x1 - d.x0; const h = d.y1 - d.y0; return w > 40 && h > 20 ? d.data.name : ''; }) .each(function(d: any) { const el = d3.select(this); const maxWidth = d.x1 - d.x0 - 8; // Truncate text if it doesn't fit let text = d.data.name; while (this.getComputedTextLength() > maxWidth && text.length > 3) { text = text.slice(0, -1); el.text(text + '…'); } }); // Value under the name cell.append('text') .attr('x', 4) .attr('y', 26) .attr('font-size', 10) .attr('fill', 'rgba(255,255,255,0.8)') .text((d: any) => { const w = d.x1 - d.x0; const h = d.y1 - d.y0; return w > 50 && h > 35 ? d3.format(',.0f')(d.value ?? 0) : ''; }); return () => { tooltip.remove(); }; }, [data, width, height, colorBy]); return <svg ref={svgRef} width={width} height={height} style={{ display: 'block' }} />; } 
Implementation details In the code above, we create a hierarchy via d3.hierarchy, configure the layout with padding, render background rectangles for groups, labels, cells with tooltip and text. Responsive behavior ensures readability on any screen.

How to add drill-down?

Drill-down is a key feature for interactive dashboards. The user clicks a group — and the treemap redraws with child-level data. We implemented a DrilldownTreemap component with breadcrumbs for navigation back.

function DrilldownTreemap({ data }: { data: TreeNode }) { const [currentNode, setCurrentNode] = useState<TreeNode>(data); const [breadcrumb, setBreadcrumb] = useState<TreeNode[]>([data]); function drillDown(node: TreeNode) { if (!node.children?.length) return; setCurrentNode(node); setBreadcrumb(prev => [...prev, node]); } function drillUp(index: number) { const target = breadcrumb[index]; setCurrentNode(target); setBreadcrumb(prev => prev.slice(0, index + 1)); } return ( <div> <nav className="flex gap-2 text-sm mb-3"> {breadcrumb.map((node, i) => ( <span key={i}> {i > 0 && <span className="text-gray-400 mx-1">/</span>} <button onClick={() => drillUp(i)} className={i === breadcrumb.length - 1 ? 'font-semibold' : 'text-blue-600 hover:underline'} > {node.name} </button> </span> ))} </nav> <Treemap data={currentNode} onCellClick={drillDown} /> </div> ); } 

Which layout algorithm to choose?

D3.js provides four tiling algorithms. Comparison:

Algorithm Feature When to use
treemapSquarify Cells as square as possible, good aspect ratio Default — best readability
treemapSliceDice Alternates horizontal and vertical slices When data has strict two-directional hierarchy
treemapSlice Only horizontal slices To display shares in one row
treemapResquarify Reuses previous layout, minimizing movement For animated data updates

We always use treemapSquarify in production — it gives the best aspect ratio. If smooth data changes are needed (e.g., stock updates), we switch to treemapResquarify. As noted in the D3.js documentation, the Squarify algorithm provides optimal aspect ratio for most cases.

How to prepare data for a treemap?

For the treemap to work, client-side data must be transformed into a hierarchy. Often the API returns a flat list of rows. The buildHierarchy function groups them by categories and subcategories:

// Transform flat data into hierarchy function buildHierarchy(items: { category: string; subcategory: string; name: string; value: number }[]): TreeNode { const root: TreeNode = { name: 'root', children: [] }; items.forEach(item => { let cat = root.children!.find(c => c.name === item.category); if (!cat) { cat = { name: item.category, children: [] }; root.children!.push(cat); } let subcat = cat.children!.find(c => c.name === item.subcategory); if (!subcat) { subcat = { name: item.subcategory, children: [] }; cat.children!.push(subcat); } subcat.children!.push({ name: item.name, value: item.value }); }); return root; } 

Work process and timing

  1. Analysis — study the data structure, define hierarchy levels, metrics for area and color. (1 day)
  2. Design — create a prototype based on your data, agree on design and interactivity. (1 day)
  3. Implementation — write the component in React + D3.js, configure tooltip, drill-down, animation. (2–3 days)
  4. Testing — test on real data: large volumes, real-time updates. (1 day)
  5. Deployment — integrate into your project, provide usage instructions. (1 day)
Deliverable Timeline
Basic treemap with tooltip and drill-down 2–3 days
With transition animation and color encoding 4–6 days
Full cycle (analysis to deployment) from 1 week

Common mistakes in treemap development

  • Ignoring paddingTop. Group headers overlap cells — text becomes unreadable. We always set paddingTop: 18.
  • Text in too small cells. We check width and height before rendering, otherwise — overflow.
  • Missing handling of empty data. If a node has value = 0, it still occupies space. Use filtering.
  • Ignoring treemapResquarify for animation. Without it, every re-render shuffles cells — users lose context.

What's included in the work and why choose us

  • Source code of the component (React + TypeScript + D3)
  • Integration with your API
  • Documentation for setup and customization
  • Deployment instructions
  • Team training (up to 2 hours)
  • 3-month warranty for uninterrupted operation

We have 5+ years of experience in data visualization on complex interfaces and 30+ completed projects with treemaps, sunbursts, and other hierarchical diagrams. Our developers are certified in React and D3.js, and all components are built from scratch, with no licensing risks. Contact us for a consultation and order a treemap that speaks for itself. Get a free project estimate within 1 business day — just send us sample data and interactivity requirements.