# Tree diagrams

> Page: https://docs.plotwork.dev/tree-diagrams. Index of every page: https://docs.plotwork.dev/llms.txt

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](#from-mermaid). |

## Ask your agent

```text
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.
```

```text
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.
```

```text
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.

**Diagram source**

```diagram
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=on
file "tsconfig.json"
file "pnpm-lock.yaml" size="412 KB"
omitted 9 "files"
```

*Picture: storefront: file tree of 17 entries (10 folders, 7 files), 4 levels, 1 changed, 9 more not drawn. The layout of a small repository: services, libraries, and the file a change touches.*

**Text description**
- services/ (folder, 3 entries): Deployable services
  - services/api/ (folder, 2 entries): REST API, a container service
    - services/api/service.toml: Service config and bindings
    - services/api/index.ts (modified, 4 KB): Hono routes
  - services/docs/ (folder): Documentation site
  - services/site/ (folder): Homepage
- libs/ (folder, 2 entries): Shared code
  - libs/engine/ (folder, 2 entries): Parser, layout, renderer
    - libs/engine/src/ (folder, 2 entries)
      - libs/engine/src/kinds/ (folder): One folder for each diagram kind
      - libs/engine/src/index.ts: Public API
    - libs/engine/manifest.json
  - libs/ui/ (folder): Design system
- scripts/ (folder): Doc generators
- README.md (emphasised): Start here
- tsconfig.json
- pnpm-lock.yaml (412 KB)
- and 9 more 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.

```plotwork
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.

**Diagram source**

```diagram
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"
```

*Picture: Pull request 128: file tree of 10 entries (4 folders, 6 files), 4 levels, 6 changed, 4 more not drawn. The files a pull request touches: two added, three modified, and one removed, with the size of each.*

**Text description**
- services/ (folder, 2 entries)
  - services/api/ (folder, 3 entries)
    - services/api/routes/ (folder, 2 entries)
      - services/api/routes/checkout.ts (added, 3 KB): The new route
      - services/api/routes/cart.ts (modified, 2 KB)
    - services/api/index.ts (modified, 4 KB): Registers the route
    - services/api/legacy-cart.ts (removed, 2 KB): Replaced by the routes
  - services/docs/ (folder, 1 entry)
    - services/docs/checkout.md (added, 6 KB): The guide
- pnpm-lock.yaml (modified, 410 KB)
- and 4 more 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.

**Diagram source**

```diagram
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
  }
}
```

*Picture: Engineering ownership: org chart of 11 entries, 3 levels. Who owns what in engineering: the CTO, three teams, and the services each team runs.*

**Text description**
- Priya Rao: CTO
  - Platform: Owns the engine and the API
    - diagrams: Parser, layout, renderer
    - api: REST API on Workers
    - mcp: MCP server
  - Growth: Owns the site and the docs
    - site: Homepage
    - docs: Guides and reference
  - Operations: Owns deploys and billing
    - deploys: Preview and production
    - billing: Plans and invoices

### 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.

**Diagram source**

```diagram
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
  }
}
```

*Picture: What the checkout needs: org chart of 9 entries, 3 levels. The services the checkout depends on, each with what it needs in turn.*

**Text description**
- Checkout (emphasised): Takes the order
  - Payments: Charges the card
    - Fraud scoring: Scores each charge
    - Ledger: Records the money
  - Inventory: Reserves the stock
    - Warehouse API: Counts stock by site
  - Tax: Works out the tax
  - Notifications: Emails the receipt
    - Email provider

### 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.

**Diagram source**

```diagram
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"
```

*Picture: Support topics: outline of 14 entries, 3 levels, 1 changed, 6 more not drawn. How the support team sorts what customers ask about.*

**Text description**
- Account: Sign in and profile
  - Sign in: Codes by email
  - API keys: Create, reveal, revoke
  - Profile
- Billing
  - Plans: Free and Pro
  - Invoices (added)
  - Refunds (emphasised)
- Diagrams
  - Drawing
    - Flow
    - Sequence
    - Schema
  - Sharing: Hosted links
- and 6 more 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](https://docs.plotwork.dev/migrating-from-mermaid.md) covers the converter in full.

```mermaid-source
---
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`):

```plotwork
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](https://docs.plotwork.dev/writing-diagrams.md) | The flow, sequence and schema languages, and the rules every kind shares |
| [Examples to copy](https://docs.plotwork.dev/examples.md) | Finished diagrams with the prompt that gets an agent to write each one |
| [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md) | Convert existing Mermaid diagrams, and what is dropped |
| [Treemaps](https://docs.plotwork.dev/treemaps.md) | Where size sits in a hierarchy, when the area matters more than the order |
| [Mind maps](https://docs.plotwork.dev/mind-maps.md) | Ideas that branch from one root, when the grouping matters more than the layout |
| [Editing existing diagrams](https://docs.plotwork.dev/editing-existing-diagrams.md) | Read a diagram back from a picture, change it, and draw it again |
| [Language reference](https://docs.plotwork.dev/reference/language.md) | Every kind, node type, option and diagnostic |
| [Diagram kinds](https://docs.plotwork.dev/reference/language/kinds.md) | The kinds Plotwork draws and the header of each |
