Recipe

Choosing a visualization for hierarchical data

A tree, a treemap, a sunburst and a Sankey all claim hierarchical data. Each requires a different input shape, and that requirement is what decides which one can say what you mean.

Treemap / Sunburst MorphOpen in new tab

Built with ApexCharts.js, ApexTree, ApexSankey

Four renderers compete for hierarchical data: a tree, a treemap, a sunburst, and a Sankey diagram. The choice is usually made on appearance, which is the wrong axis. Each one requires a different input shape, and that requirement tells you what it is able to say. Work from the data and the answer is usually forced.

Which one does your data allow?

Start with the shape you have, not the picture you want.

What your data isRendererBecause
Parent and child, no meaningful quantityTree (ApexTree)Its node type has no value field at all. It encodes structure, and nothing else.
Nesting plus a quantity per leaf, read as part-to-wholeTreemap or Sunburst (ApexCharts)Both take one nested shape with values. Area or angle carries proportion.
The above, plus a second metric per leafTreemapOnly the treemap has colorValue: size says one thing, colour says another.
Nodes with more than one parentSankey (ApexSankey)It takes an edge list, so a node may have many inputs. The other three cannot represent this at all.
Quantities that move between stages, merging and splittingSankeyEdges carry the value. In the other three, value lives on the node.

Two rows in that table decide most cases, and they are the two people skip.

Is your data actually a tree?

Three of the four require a tree: exactly one parent per node, one root. Only Sankey does not.

So the first question is not aesthetic. It is whether any node has two parents. A customer who buys from two channels, a component used by two assemblies, a person on two teams. If that happens even once, the tree, the treemap and the sunburst are all structurally wrong, and the only way to force the data in is to duplicate the node, which double-counts its value.

That is a data question you can answer with one query, before you choose a library at all:

// More than one parent anywhere? Then it is a graph, not a hierarchy.
const parents = new Map()
for (const row of rows) {
  if (!parents.has(row.id)) parents.set(row.id, new Set())
  if (row.parentId != null) parents.get(row.id).add(row.parentId)
}
const multiParent = [...parents].filter(([, set]) => set.size > 1)
console.log(multiParent.length ? 'Not a tree:' : 'A tree.', multiParent)

The same dataset, in four shapes

One flat table, as it comes out of a database, is the honest starting point:

const rows = [
  { id: 'emea',   parentId: null,   name: 'EMEA',    revenue: null },
  { id: 'de',     parentId: 'emea', name: 'Germany', revenue: 4200 },
  { id: 'fr',     parentId: 'emea', name: 'France',  revenue: 3100 },
  { id: 'apac',   parentId: null,   name: 'APAC',    revenue: null },
  { id: 'jp',     parentId: 'apac', name: 'Japan',   revenue: 5300 },
]

Tree. ApexTree takes nested nodes, and data is required even when you have nothing to put in it. There is no value field, so revenue has nowhere to go:

{ id: 'emea', name: 'EMEA', data: {}, children: [
  { id: 'de', name: 'Germany', data: { revenue: 4200 }, children: [] },
  { id: 'fr', name: 'France',  data: { revenue: 3100 }, children: [] },
]}

Your quantity can ride along in data and be rendered by a node template, but the layout will not use it. Nothing is sized by revenue.

Treemap and sunburst. One shape serves both. The type definitions call them the partition charts, and a branch may omit its own value and take the sum of its children, which is exactly what the nulls above want:

series: [{ data: [
  { x: 'EMEA', children: [
    { x: 'Germany', y: 4200 },
    { x: 'France',  y: 3100 },
  ]},
  { x: 'APAC', children: [{ x: 'Japan', y: 5300 }] },
]}]

name is accepted for x and value for y, so either naming works. Because the input is identical, switching between treemap and sunburst is a one-word change, and there is a morph demo that animates between them on one dataset.

Sankey. A flat edge list, and the hierarchy becomes edges from parent to child:

const nodes = rows.map((r) => ({ id: r.id, title: r.name }))
const edges = rows
  .filter((r) => r.parentId)
  .map((r) => ({
    source: r.parentId,
    target: r.id,
    value: r.revenue,
    type: 'revenue', // required, not optional: it is the grouping/tooltip label
  }))

sankey.render({ nodes, edges })

Two things to note. type is a required field on an edge, so a first attempt with only source, target and value will not type-check. And every edge needs a value, so intermediate nulls have to be resolved before conversion, whereas the partition charts sum them for you.

What each one can and cannot say

TreeTreemapSunburstSankey
Shows structureBestPoorly beyond 2 levelsYes, by ringYes, by column
Shows proportionNoBest, by areaYes, by angleYes, by band width
Compares leaf valuesNoYesHarder: angles at depth are thinYes
Second metric per nodeVia a templateYes, colorValueNoNo
Multiple parentsNoNoNoYes
Deep hierarchiesYes, collapsibleCrampedRings get thin fastWide, not deep
Reads exact numbersLabelsLabelsRarely fitsTooltip

The treemap's colorValue is the row worth remembering, because it is the only way in this set to put two metrics on one picture: revenue as area, growth as colour, in the same rectangle.

When none of the four is right

What you want to showReach for
Ranking twelve items, hierarchy incidentalA bar chart. A treemap makes the reader compare areas to recover an order a bar chart just states.
A hierarchy people need to read values offA data grid with tree data. Indented rows, exact numbers, sortable.
How a hierarchy changed over timeSmall multiples, or a line chart per branch. All four of these are snapshots.
Reporting lines, up to a few hundred peopleA tree, but see the org chart guide for the flat-to-nested conversion first.
Flows across several categorical dimensionsAn alluvial diagram, which is a Sankey whose stages are dimensions rather than a hierarchy.
Two levels only, and proportion is the pointA stacked bar or a pie. A treemap of six rectangles is a pie chart that is harder to read.

Which plans include these?

This is the one place the four options genuinely differ in cost, and it is worth knowing before you get attached to one.

RendererLibraryPlan
TreemapApexCharts.jsCommunity and up, so covered by the under-$2M waiver
SunburstApexCharts.jsCommunity and up, same
TreeApexTreePro and up
SankeyApexSankeyPro and up

Community is the entry plan and is free for organizations under $2M USD in annual revenue; at or above that it is a paid licence like the others. Nothing in the family is open source, and source published on GitHub is not an open licence. Every one of these renders in full without a licence key, watermarked, so you can try all four against your real data before any of this matters. The pricing page has the matrix.

One dataset, drawn as both a treemap and a sunburst

See the pieces running

Reference documentation

Frequently Asked Questions

What is the difference between a treemap and a sunburst?

Only the geometry. Both are partition charts in ApexCharts and take the identical nested input, so switching between them is a one-word change to chart.type. A treemap encodes proportion as rectangle area, which is easier to compare and to label; a sunburst encodes it as angle on concentric rings, which shows depth more clearly but gets thin fast at deeper levels.

When should I use a Sankey instead of a treemap?

When any node has more than one parent, or when the quantity moves between stages rather than belonging to a node. A Sankey takes a flat edge list, so multiple inputs per node are natural. A treemap, sunburst and tree all require exactly one parent per node, and forcing a multi-parent graph into one means duplicating nodes and double-counting their values.

Can a tree diagram show quantities?

Not in its layout. ApexTree's node type has no value field: it encodes structure only. A quantity can ride along in the required data payload and be drawn by a node template, but nothing is sized or ordered by it. If magnitude is the message, use a partition chart instead.

How do I show two metrics on one hierarchy?

Use a treemap with colorValue. It is the only one of the four that separates the two channels: size comes from the value and colour from a second metric, so revenue can be area while growth is colour in the same rectangle. Neither the sunburst nor the Sankey has an equivalent.

Which of these are included in the free Community tier?

Treemap and sunburst, because they are ApexCharts.js chart types and ApexCharts.js is in Community, which is free for organizations under $2M USD in annual revenue. ApexTree and ApexSankey are separate libraries included from the Pro plan upward, so the waiver does not extend to them. All four render in full without a licence key, watermarked, so you can compare them on your own data first.

Related

See one dataset drawn two ways

The morph demo animates the same hierarchy between a treemap and a sunburst, which is what makes the point that they are the same input.

Get started