Treemaps
A treemap draws nested areas sized by value: each tile is an item, its area is its value, and the frames round tiles are the groups they belong to. Use it to show where size, cost, or time sits in a hierarchy, such as a repository by folder, a cloud bill by service, or revenue by product line. It is for anyone, human or AI agent, who writes diagram source.
TL;DR
| Question | Answer |
|---|---|
| What is it for? | Where a total goes. The biggest tile is the biggest part, and a frame shows what a branch adds up to. |
| How do I start one? | Write treemap "Title", then group "Name" { blocks with item "Name" 120 lines inside, and } to close each group. Items may also stand at the top level. |
| How is a value written? | A number above zero right after the name, with no quotes: item "EC2" 12400. Say what it counts once, on the header, with unit="$". |
| How does it show change? | delta="8%" draws an arrow and the size of the change. better=down on the header makes a rise red, which is what a cost wants. |
| What is drawn? | A group is a frame with a corner tab that holds its icon, its name, and its total. An item is a tinted card with its icon, label, and value. Each top-level branch has its own tone, and a group inside a group is a stronger shade. |
| What if a tile is too small? | It shows what fits, whole: the label is never cut. A tile with room for nothing stays an empty card, and the text description lists every item. |
| What are the limits? | 300 items, 100 groups, and 4 groups one inside another. Past one, the source is refused with E140. More than 80 items, or many tiles too small for a label, draws a warning. |
| Does Mermaid convert? | Yes. treemap-beta and treemap become a Plotwork treemap. See From Mermaid. |
Ask your agent
Use Plotwork to draw a treemap of our monthly cloud bill by service. Group the services into Compute, Data, Network, and Operations, give the amounts in dollars, and show the change since last month, with a rise in cost in red.Use Plotwork to draw a treemap of how big each folder of this repository is in kilobytes, with the packages and the apps as groups and the largest folders first.Example
treemap "Monthly cloud spend" unit="$" better=down desc="Where the monthly cloud bill goes, by service, with the change since last month. A rise in cost is drawn red."
group "Compute" icon=lucide:cpu { item "EC2" 18400 icon=aws:ec2 delta="8%" item "Fargate" 6800 icon=aws:fargate delta="21%" item "Lambda" 4200 icon=aws:lambda delta="-12%" item "EKS" 3100 icon=aws:eks}
group "Data" icon=lucide:database { item "RDS" 9600 icon=aws:rds delta="4%" item "S3" 5200 icon=aws:s3 delta="-3%" item "DynamoDB" 3900 icon=aws:dynamodb item "OpenSearch" 3400 icon=aws:opensearch item "ElastiCache" 2800}
group "Network" icon=lucide:globe { item "Data transfer" 4800 delta="15%" item "NAT gateway" 3300 icon=aws:nat-gateway item "CloudFront" 2200 icon=aws:cloudfront delta="-6%" item "Load balancers" 1900 icon=aws:elb}
group "Operations" icon=lucide:activity { item "CloudWatch" 4600 icon=aws:cloudwatch item "Support plan" 3200 group "Security" icon=lucide:shield { item "GuardDuty" 2400 icon=aws:guardduty item "WAF" 1900 icon=aws:waf }}The four top-level groups take a tone each, in turn, and what is inside keeps it. The Security group sits inside Operations, so its frame is one step stronger than the frame round it. The green and red chips are changes since last month: the header says better=down, so a fall in cost is green and a rise is red. The line under the picture says the unit, the total, and how much there is of it.
The language
One statement to a line. Text is always in double quotes, options are key=value, and # starts a comment. Indentation is for the reader and means nothing to the parser: nesting comes from the braces, so a line written at the wrong depth cannot silently move an item.
| Statement | Meaning | Example |
|---|---|---|
treemap "Title" unit=<unit> sort=<sort> values=<on|off> better=<up|down> desc="<text>" | The header. Every option is optional, and so is the title. | treemap "Cloud spend" unit="$" better=down |
desc "<text>" | The plain-language description, as a statement. The same as desc= on the header. | desc "Where the bill goes." |
group "<Name>" tone=<tone> icon=<icon> { | Opens a frame. Write items and groups inside it, and close it with } on its own line. | group "Compute" tone=blue icon=lucide:server { |
item "<Name>" <value> tone=<tone> icon=<icon> delta=<change> | A tile whose area is its value. | item "EC2" 12400 icon=aws:ec2 delta="8%" |
} | Closes the nearest group that is still open, on a line of its own. | } |
A group is sized by what it holds, so it has no value of its own, and a group with nothing in it is refused. An item has no id, because a treemap has no cross references.
treemap "Disk use" unit="GB"
group "Home" icon=lucide:folder { item "Photos" 420 icon=lucide:folder item "Documents" 85}
item "Backups" 310Header options
| Option | Values | Default | Meaning |
|---|---|---|---|
unit= | a short word or symbol, at most 12 characters | none | What the values count. $, €, £, and ¥ lead the number ($1,240), % follows it (85%), and any other unit follows after a space (120 MB). |
sort= | desc, written | desc | desc draws the largest first, which packs best. written keeps the order of the source, for an order that means something. |
values= | on, off | on | off hides the value on each tile and the total in each tab. The line under the picture still says the total. |
better= | up, down | up | Which way of a change is good news and so drawn green. Use up for sizes, coverage, and revenue, and down for cost and errors. |
desc= | quoted text | none | The plain-language description. |
Item and group options
| Option | On | Values | Default |
|---|---|---|---|
tone= | item, group | neutral, blue, violet, aqua, orange, green, amber, red | the tone of the branch it is in |
icon= | item, group | any bundled icon, such as lucide:server or aws:lambda, or none | none |
delta= | item | a number, with an optional sign and an optional percent sign | none |
Writing a change
A change is the difference since the last period, in the unit of the values or in percent. Its chip shows an arrow and the size of the change, so the arrow carries the sign. A rise can be a bare number. A fall, and any percent, is written in double quotes, because the language reads a bare - as part of an arrow and does not read a bare % at all.
| Written | Drawn |
|---|---|
delta=5 or delta="+5" | An up arrow and 5 |
delta="-5" | A down arrow and 5 |
delta="12%" | An up arrow and 12% |
delta="-12.5%" | A down arrow and 12.5% |
delta="0" | Nothing |
A bare delta=-5 is refused with two errors, one for the - the lexer cannot read and one that says to quote it, so a fall is never drawn as a rise. The chip is green when its direction is the one better= names and red when it is not. When any item has a change, the line under the picture carries a key to the two chips.
Sorting and order
By default the largest tile comes first and ties keep the order written, which packs the picture best and puts the biggest part where it is read first. sort=written keeps the order of the source instead, here the order the product lines were launched in. Revenue is good news when it rises, so better=up (the default) draws a rise green.
treemap "Revenue by product line" unit="$" sort=written better=up desc="Yearly revenue by product line, in the order the lines were launched, with the change since last year. A rise is good news and drawn green."
group "Subscriptions" tone=blue icon=lucide:layers { item "Starter" 420000 delta="9%" item "Team" 880000 delta="22%" item "Enterprise" 1450000 delta="14%"}
group "Usage" tone=aqua icon=lucide:server { item "API calls" 640000 delta="31%" item "Storage" 310000 delta="-4%" item "Egress" 190000 delta="-11%"}
group "Services" tone=amber icon=lucide:users { item "Setup" 340000 item "Training" 260000 delta="-18%" item "Support" 410000 delta="6%"}A value of 100,000 or more is drawn compactly on a tile ($1.4M, $420K), and in full in the text description.
Colour and nesting
The top-level groups and items take blue, orange, aqua, violet, amber, and neutral in turn, and everything inside a group keeps its tone. tone= on a group or an item changes it for that branch. Green and red are left out of the rotation because they are what a change chip means, so a branch is never mistaken for news; choose them with tone= when they should mean something. A group inside a group is drawn one step stronger, to four levels, so a hierarchy of region, account, team, and service reads as four shades. Nobody writes a color value: the theme owns every color in light and dark.
Groups nest as the data does, with items at any level:
treemap "Repository size by folder" unit="KB" sort=desc desc="How much of the repository each folder takes, in kilobytes, down to the files that matter."
group "packages" icon=lucide:package { group "diagrams" icon=lucide:folder { group "src" { item "kinds" 4800 icon=lucide:folder item "icons" 9200 icon=lucide:layers item "layout" 1300 item "render" 1100 item "parse" 900 item "import" 1600 item "constants" 2100 } item "fixtures" 1400 item "scripts" 650 } item "ui" 2400 icon=lucide:box}
group "apps" icon=lucide:monitor { item "docs" 7200 icon=lucide:book-open item "site" 3100 icon=lucide:globe item "api" 2600 icon=lucide:server item "mcp" 800 item "app" 1500 item "admin" 900}
group "docs" icon=lucide:file-text tone=violet { item "architecture" 640 item "research" 420 item "standards" 310}
item "pnpm-lock.yaml" 5600 icon=lucide:lockitem "node_modules cache" 2200item "README and config" 180What a tile shows
A tile shows the richest of these that it has room for, whole. A label is wrapped at word breaks, to at most three lines, and is never cut: a tile too narrow for its longest word shows no label, and the full text is in the text description.
| Rung | A tile shows |
|---|---|
| 1 | The icon on a chip, the label, the value, and the change |
| 2 | The label, the value, and the change |
| 3 | The icon on a chip, the label, and the value |
| 4 | The label and the value |
| 5 | The icon on a chip and the label |
| 6 | The label |
| 7 | The icon alone |
| None | An empty card |
A group's tab gives up its icon first, then its total, then cuts its name with an ellipsis and warning W203, as a flow group does. A frame too small for a readable name has no tab. A tile less than 4px wide or high is not drawn at all, and lint says which.
Limits and warnings
| Limit | Value | What happens past it |
|---|---|---|
| Items | 300 | E140 before any layout, with the advice to group the small ones into one "Other" item or split the treemap. |
| Groups | 100 | E140. |
| Groups one inside another | 4 | E140 once for each group past the limit. Its items join the group around it, so nothing else is reported. |
| A value | above 0 and at most one trillion | E136. |
| A unit | 12 characters | E136. |
| Items before the density warning | 80 | W200. |
| Levels before the warning | 3 | W200. |
The picture is 880px wide, the width a Markdown column keeps text readable at, and the treemap in it grows taller with the number of items and groups, from 320px to 1100px. The layout is plain arithmetic, so a treemap of 300 items is laid out in a few milliseconds.
Every mistake comes back as a result with a code, a line, a column, and a hint. Warnings never stop the drawing.
| Code | When it fires | Fix |
|---|---|---|
E100 | An item with no value, an item name that is not in quotes, a group with no block or with no items, a treemap with no items, or text after a statement. | Read the hint, which names the line to write. |
E101, E110, E121, E122 | An unterminated string, an unknown statement, an unknown tone, or an unknown icon. | Read the hint, which names the nearest choice. |
E133, E134 | A group that is never closed, or a } with nothing to close. | Add or remove the brace. |
E135 | An unknown option, or value=. | Write the value right after the name: item "Name" 120. |
E136 | An unknown sort=, values=, or better=, a bad unit, a value that is not a number or is zero, or a delta= that is not a change. | Read the hint, which says what to write. |
E140 | More than 300 items, more than 100 groups, or groups nested more than 4 deep. | Split the treemap by top-level group, or flatten it. |
W200 | More than 80 items, groups nested more than 3 deep, many items too small to show a label, or items too small to draw. | Group the smallest items into one "Other" item, or split the treemap by group. |
W202 | The picture is wider than a Markdown column and its text would shrink. | Shorten the legend text, such as a long unit. |
W203 | A group name was cut with an ellipsis. | Shorten the name. |
From Mermaid
Mermaid treemap-beta (and treemap, which Mermaid is renaming it to) converts to a treemap. A node with a value is an item and a node without one is a group, and a node is a child of the nearest node above it that is indented less, as Mermaid reads it. Ask your agent to convert it, or use the convert_mermaid tool. The migration guide covers the converter in full.
---title: Product budget---treemap-beta"Engineering" "Platform": 450000 "Application": 380000:::important "Tooling": 0"Marketing" "Campaigns": 210000 "Events": 90000:::error"Support": 160000
classDef important fill:#f96The converter reports the class and its definition it dropped (M200), the zero it dropped (M216), and the tone it took from the class error (M207):
treemap "Product budget"
group "Engineering" { item "Platform" 450000 item "Application" 380000}
group "Marketing" { item "Campaigns" 210000 item "Events" 90000 tone=red}
item "Support" 160000| Mermaid | Plotwork | Report |
|---|---|---|
"Name": 12 and "Name", 12 | item "Name" 12 | none |
| A name with nodes under it | group "Name" { | none |
A value with , or _ marks, such as 1,200 | 1200 | none |
Single quotes and a trailing ; | read as written | none |
A title line, front matter title:, and accTitle: | The title. A title line wins. | none |
accDescr: | desc= | none |
:::error and the like | tone=red for a class that names a tone (error, warning, success, and the rest of the shared table) | M207 |
Any other :::class, classDef, and style | Dropped. | M200 |
Front matter config: such as valueFormat and showValues, and %%{init}%% | Dropped. Put the unit on the header with unit=. | M200 |
<br/> and other HTML in a name | Removed. A line break becomes a space. | M205 |
| A leaf with a value of zero, a group with nothing left in it, and a node with no name | Dropped. | M216 |
| A node under a leaf, a line that is not a node, a value that is not a number, and text after the header | Nothing is converted, or the line is skipped when the conversion is lenient. | M110, or M218 |
| More than 300 nodes, or more than 4 groups one inside another | Nothing is converted. | M102 |
| No title | Converted. | M214 |
See also
| Doc | Why |
|---|---|
| Writing diagrams | The flow, sequence and schema languages, and the rules every kind shares |
| Examples to copy | Finished diagrams with the prompt that gets an agent to write each one |
| Migrating from Mermaid | Convert existing Mermaid diagrams, and what is dropped |
| Mind maps | Ideas that branch from one root, when the grouping matters more than the size |
| Gantt charts | A plan drawn as tasks along a calendar |
| Timelines | Events along a time axis, when the order matters more than the size |
| Language reference | Every kind, node type, option and diagnostic |
| Diagram kinds | The kinds Plotwork draws and the header of each |