Tree diagrams
A tree diagram draws a hierarchy: the layout of a repository, the files a pull request changes, a company's teams, who owns which service, or a taxonomy. It replaces the ASCII tree that gets pasted into a README. It is for anyone, human or AI agent, who writes diagram source.
TL;DR
| Question | Answer |
|---|---|
| What is it for? | Anything with a parent and children: a repository layout, a diff, an org chart, an ownership map, a service dependency tree, or a list of topics. |
| How do I start one? | Write tree "Title", then dir "Name" { blocks with file "name" lines inside, and } to close each block. Org charts and outlines use node "Name" instead. |
| Which styles are there? | style=files (the default) draws a file tree in a frame. style=org draws cards joined by lines, growing down or right. style=outline draws a compact bulleted list. |
| How is a file's icon chosen? | From its name. TypeScript, JavaScript, JSON, Markdown, CSS, HTML, YAML, TOML, lock files, images, shell, Python, Go, Rust, Java, SQL, Dockerfile, and .gitignore each get one. icon= overrides it. |
| How do I show a diff? | change=added, change=modified, or change=removed washes the row green, amber, or red and adds a chip. size="4 KB" adds a pill. |
| How do I keep it short? | omitted 14 "files" draws "and 14 more files". counts=on shows how many entries each folder holds. |
| What are the limits? | 300 entries and 8 levels. Past one, the source is refused with E140. More than 60 rows or 6 levels draws a warning. |
| Does Mermaid convert? | Yes. treeView-beta and treeView become a Plotwork tree. See From Mermaid. |
Ask your agent
Use Plotwork to draw the layout of this repository as a tree. Show the top two levels, put a short note beside each folder, and highlight the README.Use Plotwork to draw the files this pull request changes as a tree. Mark each file added, modified, or removed, give the size of each, and say how many unchanged files are left out.Use Plotwork to draw an org chart of the engineering team as a tree. The CTO has three teams, and each team owns a few services. Give each team its own tone.Example
A repository layout: folders with a note on each, the icon of each file chosen from its name, a file that changed, a file that is emphasised, and a line that says how many more files there are.
tree "storefront" desc="The layout of a small repository: services, libraries, and the file a change touches."
dir "services" note="Deployable services" icon=lucide:layers { dir "api" note="REST API, a container service" icon=lucide:server { file "service.toml" note="Service config and bindings" file "index.ts" note="Hono routes" change=modified size="4 KB" } dir "docs" note="Documentation site" icon=lucide:book-open dir "site" note="Homepage" icon=lucide:globe}
dir "libs" note="Shared code" icon=lucide:package { dir "engine" note="Parser, layout, renderer" { dir "src" { dir "kinds" note="One folder for each diagram kind" file "index.ts" note="Public API" } file "manifest.json" } dir "ui" note="Design system"}
dir "scripts" note="Doc generators"file "README.md" note="Start here" mark=onfile "tsconfig.json"file "pnpm-lock.yaml" size="412 KB"omitted 9 "files"The title is the root of the tree, in the corner tab of the frame. Each folder is a row with its chip, its name in bold, and its note in a quiet column. The thin lines join a folder to its entries. The changed file is washed in amber with a chip, the README is emphasised in blue, and the three dots are the entries that are left out. Folders with no icon of their own get the folder icon, and files get the icon of their type.
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 entry. A name with a dot or a slash needs its quotes, as every name does.
| Statement | Meaning | Example |
|---|---|---|
tree "Title" style=<style> direction=<direction> counts=<on|off> desc="<text>" | The header. Every option is optional, and so is the title. | tree "storefront" style=files |
desc "<text>" | The plain-language description, as a statement. The same as desc= on the header. | desc "The layout of the repository." |
dir "<Name>" <options> { | A folder, a team, or a group. Write its entries inside it and close it with } on its own line. Leave the { off for a folder with nothing drawn in it. | dir "src" note="source code" { |
file "<name>" <options> | A file. It holds nothing. | file "main.ts" note="entry point" |
node "<Name>" <options> { | A person, a team, a service, or a topic. The braces are optional for a leaf. | node "Platform" note="12 engineers" { |
omitted <count> "<noun>" | A quiet entry that says how many more exist and are not drawn. | omitted 14 "files" |
} | Closes the nearest block that is still open, on a line of its own. | } |
An entry has no id, because a tree has no cross references. A service that two others depend on is written under each of them.
tree "Notes" style=outline
node "Plans" { node "Free" node "Pro" note="Hosted links"}omitted 3 "topics"Header options
| Option | Values | Default | Meaning |
|---|---|---|---|
style= | files, org, outline | files | How the tree is drawn. |
direction= | down, right | down | Which way an org chart grows. Only org reads it. |
counts= | on, off | off | Adds a pill to each folder with the number of entries it holds. The entries an omitted line stands for are counted. |
desc= | quoted text | none | The plain-language description. |
Entry options
| Option | Values | Default | Meaning |
|---|---|---|---|
note= | quoted text | none | The quiet text after the name in a file tree, or the second line of a card. |
icon= | any bundled icon, such as lucide:server, or none | a folder, or the icon of the file's type | The icon on the chip. |
tone= | neutral, blue, violet, aqua, orange, green, amber, red | none | Washes the row, or tints the card, in the tone. It applies to that entry alone. |
change= | added, modified, removed | none | Washes the row green, amber, or red and adds a chip with the word. It wins over tone=. |
size= | quoted text of at most 12 characters | none | A pill at the right of the row. An org card does not draw it. |
mark= | on, off | off | Emphasises the entry: a stronger blue wash and a bar at the left of a row, or a heavier outline on a card. |
The second line of an entry is note= in every style. sub=, which flow nodes use, is answered with that sentence.
Choosing the icon of a file
A file's icon comes from its name, so a plain file "main.ts" already has the TypeScript mark. The table is small on purpose.
| Name | Icon |
|---|---|
.ts, .tsx | TypeScript |
.js, .jsx, .mjs, .cjs | JavaScript |
.json, .jsonc | Braces |
.md, .mdx | An open book |
.css, .scss | Code |
.html, .xml | Markup |
.yaml, .yml, .toml, .ini, .env | Settings |
A name with lock in it, such as pnpm-lock.yaml | A padlock |
.svg, .png, .jpg, .gif, .webp | A frame |
.sh, .bash, .zsh | A terminal |
.py, .go, .rs, .java | The language's mark |
.sql | A database |
Dockerfile, a compose file | Docker |
.gitignore, .gitattributes | Git |
| Any other name | A plain file |
A pull request as a diff
change= turns a tree into a diff. A folder that holds changes can carry one too, and counts=on shows how many entries each folder holds. omitted says how many unchanged files are not drawn, so the picture stays about the change.
tree "Pull request 128" counts=on desc="The files a pull request touches: two added, three modified, and one removed, with the size of each."
dir "services" { dir "api" { dir "routes" { file "checkout.ts" change=added size="3 KB" note="The new route" file "cart.ts" change=modified size="2 KB" } file "index.ts" change=modified size="4 KB" note="Registers the route" file "legacy-cart.ts" change=removed size="2 KB" note="Replaced by the routes" } dir "docs" { file "checkout.md" change=added size="6 KB" note="The guide" }}file "pnpm-lock.yaml" change=modified size="410 KB"omitted 4 "files"A row is washed in the tone of its change, then in its own tone, then in blue when it is emphasised. The pills line up down the page, so sizes and changes can be compared at a glance.
An org chart
style=org draws cards in a tidy tree: a parent is centred over its children, and every card of a level has the same height. Use node for people and teams, note= for a role, and tone= to colour a team. A chart that is wider than a Markdown column lists the leaves under their parent on its own, so a team of twelve does not spread across the page.
tree "Engineering ownership" style=org desc="Who owns what in engineering: the CTO, three teams, and the services each team runs."
node "Priya Rao" note="CTO" icon=lucide:user tone=blue { node "Platform" note="Owns the engine and the API" icon=lucide:layers tone=violet { node "diagrams" note="Parser, layout, renderer" icon=lucide:package node "api" note="REST API on Workers" icon=lucide:server node "mcp" note="MCP server" icon=lucide:plug } node "Growth" note="Owns the site and the docs" icon=lucide:rocket tone=aqua { node "site" note="Homepage" icon=lucide:globe node "docs" note="Guides and reference" icon=lucide:book-open } node "Operations" note="Owns deploys and billing" icon=lucide:settings tone=orange { node "deploys" note="Preview and production" icon=lucide:rocket node "billing" note="Plans and invoices" icon=lucide:credit-card }}A service dependency tree
direction=right grows the chart to the right, a column for each level. It suits a dependency tree, where a service has a long name and a note, because the levels stack down the page and not across it. The cards of a column share a width.
tree "What the checkout needs" style=org direction=right desc="The services the checkout depends on, each with what it needs in turn."
node "Checkout" note="Takes the order" icon=lucide:credit-card tone=blue mark=on { node "Payments" note="Charges the card" icon=simple:stripe tone=violet { node "Fraud scoring" note="Scores each charge" icon=lucide:shield node "Ledger" note="Records the money" icon=lucide:database } node "Inventory" note="Reserves the stock" icon=lucide:boxes tone=aqua { node "Warehouse API" note="Counts stock by site" icon=lucide:warehouse } node "Tax" note="Works out the tax" icon=lucide:receipt-text node "Notifications" note="Emails the receipt" icon=lucide:mail { node "Email provider" icon=lucide:send }}An outline
style=outline is the plainest: a bulleted list that steps in by level, with no frame. It reads as a table of contents, and it is the right choice for a taxonomy or a list of topics. Notes, changes, emphasis, and omitted all work the same way.
tree "Support topics" style=outline desc="How the support team sorts what customers ask about."
node "Account" note="Sign in and profile" { node "Sign in" note="Codes by email" node "API keys" note="Create, reveal, revoke" node "Profile"}
node "Billing" icon=lucide:credit-card { node "Plans" note="Free and Pro" node "Invoices" change=added node "Refunds" mark=on}
node "Diagrams" { node "Drawing" { node "Flow" node "Sequence" node "Schema" } node "Sharing" note="Hosted links"}omitted 6 "topics"What a tree is made of
| Part | How it looks |
|---|---|
| Frame | A file tree sits in a rounded frame with the title in a corner tab, as a flow group does. An outline and an org chart have no frame. |
| Row | The same height for every entry. A note of one line never makes it taller, and a note of two lines adds a line. Names step in by level. |
| Notes | One column after the longest name. They wrap to two lines to keep the picture inside a Markdown column, and are cut with an ellipsis after that. |
| Pills | The count, the size, and the change, in columns at the right. |
| Guide lines | From under a folder's chip a thin line runs down to its last entry and turns into it with a rounded corner. |
| Card | A tinted flow node with an icon chip, a name of at most two lines, and a note of at most two lines. |
| Connectors | Square bends with rounded corners, through the gap between two levels. |
Nobody writes a color value: the theme owns every color in light and dark.
Limits and warnings
| Limit | Value | What happens past it |
|---|---|---|
| Entries | 300, the omitted lines included | E140 before any layout, with the advice to draw one tree for each top-level folder. |
| Levels | 8 | E140, once at the first folder whose block would start a ninth level. Its entries join the level around it, so nothing else is reported. |
| A size | 12 characters | E136. |
| Rows before the density warning | 60, in a file tree or an outline | W200. |
| Levels before the warning | 6 | W200. |
| A name | 400px | Cut with an ellipsis and W203. |
| A note | Two lines | Cut with an ellipsis and W203. |
A file tree is as wide as its names, notes, and pills need, up to 880px, the width a Markdown column keeps text readable at. An org chart is as wide as its levels need, and when it is wider than a column it lists leaves under their parents first. The layout is plain arithmetic, so a tree of 300 entries 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 | A name that is not in quotes, an empty name, a file with a block, omitted with no count, a tree with no entries, or text after a statement. | Read the hint, which names the line to write. A bare word where a name belongs is answered with the form without an id. |
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 block that is never closed, or a } with nothing to close. | Add or remove the brace. |
E135 | An unknown option, or sub=. | Use note= for the second line. |
E136 | An unknown style=, direction=, counts=, change=, or mark=, a size that is too long, or an omitted count that is not from 1 to 999,999. | Read the hint, which says what to write. |
E140 | More than 300 entries, or more than 8 levels. | Split the tree by top-level folder, or flatten it. |
W200 | More than 6 levels, more than 60 rows, or two entries of one name in one folder of a file tree. | Lift the inner entries up, fold small folders into an omitted line, or rename the repeat. |
W202 | The picture is wider than a Markdown column and its text would shrink. | Set direction=right, shorten the names, or split the chart by branch. |
W203 | A name, a note, or the title was cut with an ellipsis. | Shorten it. The full text stays in the text description. |
From Mermaid
Mermaid treeView-beta (and treeView, which Mermaid is likely to rename it to) converts to a file tree. An entry with entries under it is a folder, so is a name that ends with a slash, and any other entry is a file. An entry is a child of the nearest entry 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: Repository layout---treeView-beta "src" ## where the code lives "main.ts" :::error ## the entry point "utils" icon(folder) "docs" "guide.md" "README.md"The converter reports the class it turned into a tone (M207) and the icon it dropped (M215):
tree "Repository layout" style=files
dir "src" note="where the code lives" { file "main.ts" note="the entry point" tone=red file "utils"}
dir "docs" { file "guide.md"}
file "README.md"| Mermaid | Plotwork | Report |
|---|---|---|
| An entry with entries under it | dir "Name" { | none |
| An entry with none | file "Name" | none |
| A name that ends in a slash | dir, with the slash taken off | none |
| A name in double quotes, single quotes, or bare text | the name | none |
## text after a name | note="text" | 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, and classDef | Dropped. | M200 |
icon(name) | Dropped. Plotwork icons are a closed set, so the hint names the closest bundled icon. | M215 |
Front matter config: such as rowIndent and lineThickness, and %%{init}%% | Dropped. | M200 |
<br/>, other HTML, and entities in a name | Removed or decoded. A line break becomes a space. | M205 |
| An entry with no name | Dropped, with everything under it. | M216 |
| A line that is not an entry | Nothing is converted, or the line is skipped when the conversion is lenient. | M110, or M218 |
| More than 300 entries, or more than 8 levels | 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 |
| Treemaps | Where size sits in a hierarchy, when the area matters more than the order |
| Mind maps | Ideas that branch from one root, when the grouping matters more than the layout |
| Editing existing diagrams | Read a diagram back from a picture, change it, and draw it again |
| Language reference | Every kind, node type, option and diagnostic |
| Diagram kinds | The kinds Plotwork draws and the header of each |