Render Mermaid diagrams in your terminal, in Claude Code, or in your Python app.
- Draws the Mermaid diagrams in Claude Code's replies as they stream (setup). It also draws the Mermaid blocks in Markdown files
- 18 diagram types: flowcharts, sequence, class, ER, state, block, git, gantt, architecture, pie, treemap, mindmap, timeline, kanban, quadrant, XY chart, user journey, and packet
- No dependencies. The core is pure Python
- Fits the terminal width: it tightens spacing first, then wraps labels at word boundaries
- Colored output with Rich and a TUI widget for Textual, as optional extras, with 11 themes
--asciifor terminals without box-drawing characters- Reads from a file or stdin:
cat diagram.mmd | termaid
pip install termaidbrew install termaidOr try it without installing:
uvx termaid diagram.mmdThe plugin carries its own copy of termaid. It needs Python 3.9 or later as python3, which macOS and most Linux systems already have. Run this in Claude Code 2.1.275 or later:
/plugin install termaid --marketplace fasouto/termaid
Claude Code then draws the Mermaid diagrams in Claude's replies. Ask for a diagram in plain words, for example:
- "Draw a diagram of the main components of this app and how they connect"
- "Show what happens when a user logs in, as a sequence diagram"
- "Draw the database models and how they relate"
- "Draw the states an order goes through, from cart to delivery"
- "Turn this migration plan into a gantt chart"
Claude Code covers older versions and a setup without the plugin.
termaid diagram.mmd
echo "graph LR; A-->B-->C" | termaid
termaid diagram.mmd --theme neon
termaid diagram.mmd --ascii
termaid --markdown README.md # draw every ```mermaid block in a document
gh pr view 42 | termaid --markdownfrom termaid import render
print(render("graph LR\n A --> B --> C"))# Colored output (requires: pip install termaid[rich])
from termaid import render_rich
from rich import print as rprint
rprint(render_rich("graph LR\n A --> B", theme="terra"))# Textual TUI widget (requires: pip install termaid[textual])
from termaid import MermaidWidget
widget = MermaidWidget("graph LR\n A --> B")All directions supported: LR, RL, TD/TB, BT.
graph TD
A[Start] --> B{Is valid?}
B -->|Yes| C(Process)
C --> D([Done])
B -->|No| E[Error]
┌─────────────┐
│ │
│ Start │
│ │
└──────┬──────┘
│
│
▼
┌──────◇──────┐
│ │
│ Is valid? │
│ │
└──────◇──────┘
│
│
├──────────────────╮No
Yes│ │
▼ ▼
╭─────────────╮ ┌─────────────┐
│ │ │ │
│ Process │ │ Error │
│ │ │ │
╰──────┬──────╯ └─────────────┘
│
│
▼
╭─────────────╮
( )
( Done )
( )
╰─────────────╯
Node shapes: rectangle [text], rounded (text), diamond {text}, stadium ([text]), subroutine [[text]], circle ((text)), double circle (((text))), hexagon {{text}}, cylinder [(text)], asymmetric >text], parallelogram [/text/], trapezoid [/text\], and @{shape} syntax
Edge styles: solid -->, dotted -.->, thick ==>, bidirectional <-->, circle endpoint --o, cross endpoint --x, labeled -->|text|, variable length --->, ---->
Styling: classDef, style, linkStyle directives, :::className suffix
Subgraphs: nesting, cross-boundary edges, per-subgraph direction override
Other: %% comments, ; line separators, Markdown labels "`**bold** *italic*`", & operator (A & B --> C), entity codes (#quot;, #amp;, #9829;)
sequenceDiagram
Alice->>Bob: Hello Bob
Bob-->>Alice: Hi Alice
Alice->>Bob: How are you?
Bob-->>Alice: Great!
┌─────────┐ ┌───────┐
│ Alice │ │ Bob │
└─────────┘ └───────┘
┆ Hello Bob ┆
──────────────────►
┆ Hi Alice ┆
◄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
┆ How are you? ┆
──────────────────►
┆ Great! ┆
◄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
┆ ┆
Message types: solid arrow ->>, dashed arrow -->>, solid line ->, dashed line -->
Participants: participant, actor, aliases
classDiagram
class Animal {
+String name
+int age
+makeSound()
}
class Dog {
+String breed
+fetch()
}
Animal <|-- Dog
┌──────────────┐
│ Animal │
├──────────────┤
│ +String name │
│ +int age │
├──────────────┤
│ +makeSound() │
└──────────────┘
△
│
│
│
┌───────────────┐
│ Dog │
├───────────────┤
│ +String breed │
├───────────────┤
│ +fetch() │
└───────────────┘
Relationships: inheritance <|--, composition *--, aggregation o--, association --, dependency ..>, realization ..|>
Members: attributes and methods with visibility (+ public, - private, # protected, ~ package)
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
┌──────────────┐
│ CUSTOMER │
└──────────────┘
│1
│ places
│
│0..*
┌──────────────┐
│ ORDER │
└──────────────┘
│1
│ contains
│
│1..*
┌──────────────┐
│ LINE-ITEM │
└──────────────┘
Cardinality: || (exactly one), o| (zero or one), }| (one or more), o{ (zero or more)
Line styles: solid --, dashed ..
Attributes: type, name, keys (PK, FK, UK), comments
stateDiagram-v2
[*] --> Idle
Idle --> Processing : start
Processing --> Done : complete
Done --> [*]
╭───────◯──────╮
│ │
│ ● │
│ │
╰───────◯──────╯
│
│
▼
╭──────────────╮
│ │
│ Idle │
│ │
╰───────┬──────╯
│
start│
▼
╭──────────────╮
│ │
│ Processing │
│ │
╰───────┬──────╯
│
complete│
▼
╭──────────────╮
│ │
│ Done │
│ │
╰───────┬──────╯
│
│
▼
╭───────◯──────╮
│ │
│ ◉ │
│ │
╰───────◯──────╯
Features: [*] start/end states, transition labels, state "name" as alias, composite states (state Parent { }), stereotypes (<<choice>>, <<fork>>, <<join>>)
block-beta
columns 3
A["Frontend"] B["API"] C["Database"]
┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ │ │ │ │
│ Frontend │ │ API │ │ Database │
│ │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘
Features: columns N, column spanning (blockname:N), links between blocks, nested blocks
gitGraph
commit id: "init"
commit id: "feat"
branch develop
commit id: "dev-1"
commit id: "dev-2"
checkout main
commit id: "fix"
merge develop id: "merge"
main ───●─────●──────┼──────────────●──────●─
init feat │ fix merge
│ │
develop ●───────●─────────────┼
dev-1 dev-2
Directions: LR (default), TB, BT
Commands: commit (with id:, type:, tag:), branch (with order:), checkout/switch, merge, cherry-pick
Commit types: NORMAL (●), REVERSE (✖), HIGHLIGHT (■)
Config: %%{init: {"gitGraph": {"mainBranchName": "master"}}}%%
gantt
title Project Plan
dateFormat YYYY-MM-DD
section Design
Wireframes :done, des1, 2024-01-01, 2024-01-14
Prototypes :active, des2, 2024-01-08, 2024-01-21
section Development
Frontend :dev1, after des2, 30d
Backend :crit, dev2, 2024-01-15, 2024-03-01
Project Plan
Design │
Wireframes │░░░░░░░░░░░░░
Prototypes │ ▓▓▓▓▓▓▓▓▓▓▓▓▓
Development │
Frontend │ ██████████████████████████████
Backend │ ███████████████████████████████████████████████
└┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬
Jan 01 Jan 11 Jan 21 Jan 31 Feb 10 Feb 20 Mar 01
Tasks: done, active, crit, milestone tags, after id dependencies, durations in Mermaid's units (30d, 2w, 3M, 1.5h, 45m, 1.2s, 406ms)
Time scales: any span from years to milliseconds. dateFormat YYYY-MM-DD HH:mm keeps the time of day, dateFormat x and X read Unix milliseconds and seconds, axisFormat %H:%M (or %S.%L) labels the axis, and tickInterval 6hour places the ticks
Yes, the syntax says pie. No, we don't draw a circle. I know. Have you ever tried to read a pie chart made of █ and ▓? Exactly. We render them as horizontal bar charts instead.
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
Pets adopted by volunteers
████████████████████████████████░░░░░░░▒
79% 17%
Dogs┃████████████████████████████████ 79.4%
Cats┃░░░░░░░ 17.5%
Rats┃▒ 3.1%
Features: title, showData (display raw values), %% comments
treemap-beta
"Frontend"
"React": 40
"CSS": 15
"Backend"
"API": 35
"Auth": 10
┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
┆ Frontend ┆ ┆ Backend ┆
┆┌───────────────────┐ ┌──────┐┆ ┆┌────────────────┐ ┌────┐┆
┆│ React │ │ CSS │┆ ┆│ API │ │Auth│┆
┆│ 40 │ │ 15 │┆ ┆│ 35 │ │ 10 │┆
┆│ │ │ │┆ ┆│ │ │ │┆
┆└───────────────────┘ └──────┘┆ ┆└────────────────┘ └────┘┆
└┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
Features: nested sections via indentation, "label": value syntax, proportional sizing, %% comments
mindmap
Project
Design
Wireframes
Mockups
Development
Frontend
Backend
Testing
╭─ Design ──╭─ Wireframes
│ ╰─ Mockups
Project ──├─ Development ──╭─ Frontend
│ ╰─ Backend
╰─ Testing
Features: indentation-based nesting, automatic overflow to the left when many children, rounded/sharp/ASCII branch characters, Mermaid shape markers stripped ((round), [square], {{hexagon}}, )cloud()
xychart-beta
title "Monthly Revenue"
x-axis [Jan, Feb, Mar, Apr, May, Jun]
bar [12, 18, 25, 20, 30, 35]
Monthly Revenue
35 │ ████
│ ████
│ ▄▄▄▄ ████
28 │ ████ ████
│ ▄▄▄▄ ████ ████
│ ████ ████ ████
21 │ ████ ▄▄▄▄ ████ ████
│ ▄▄▄▄ ████ ████ ████ ████
│ ████ ████ ████ ████ ████
14 │ ████ ████ ████ ████ ████
│████ ████ ████ ████ ████ ████
│████ ████ ████ ████ ████ ████
7 │████ ████ ████ ████ ████ ████
│████ ████ ████ ████ ████ ████
│████ ████ ████ ████ ████ ████
0 └──┬─────┬─────┬─────┬─────┬─────┬─
Jan Feb Mar Apr May Jun
Features: bar charts, line charts, bar+line combos, horizontal orientation (xychart horizontal), axis labels, xychart and xychart-beta keywords, rounded/sharp line corners, JSON ingest
journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me
My working day
● Cat
◆ Me
╭───────────── Go to work ──────────────╮ ╭───────── Go home ───────────╮
╭◆─────────╮ ╭◆────────────╮ ╭●◆───────╮ ╭◆──────────────╮ ╭◆─────────╮
─│ Make tea │─│ Go upstairs │─│ Do work │────│ Go downstairs │─│ Sit down │────►
╰──────────╯ ╰─────────────╯ ╰─────────╯ ╰───────────────╯ ╰──────────╯
😄 😐 😞 😄 😄
Features: sections, satisfaction scores (😞-😄), multi-actor support with distinct symbols (●◆■▲), rounded/sharp/ASCII corners
packet
0-15: "Source Port"
16-31: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
0 15 16 31
╭───────────────────────────────────────────────┬───────────────────────────────────────────────╮
│ Source Port │ Destination Port │
╰───────────────────────────────────────────────┴───────────────────────────────────────────────╯
32 63
╭───────────────────────────────────────────────────────────────────────────────────────────────╮
│ Sequence Number │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
64 95
╭───────────────────────────────────────────────────────────────────────────────────────────────╮
│ Acknowledgment Number │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
Features: bit-aligned field layouts, boundary numbers, auto-increment (+N) syntax, separated boxes per row, truncated label legend with bit ranges, packet and packet-beta keywords
When Mermaid source arrives a piece at a time (an LLM writing a reply, a file being
typed), pass partial=True to redraw it as it grows:
from termaid import render
print(render(source_so_far, partial=True))The last line, unless the source ends with a newline, is treated as unfinished: open brackets and quotes are closed, so a node's label shows the text so far, and an edge or message whose target hasn't arrived yet is left out until it has.
| Flag | Description |
|---|---|
--ascii |
ASCII-only output (no Unicode box-drawing) |
--theme NAME |
Color theme (requires pip install termaid[rich]). Use --themes to list |
--themes |
List all available color themes |
--demo [TYPE] |
Render sample diagrams (all, flowchart, sequence, mindmap, etc.) |
--padding-x N |
Horizontal padding inside boxes (default: 4) |
--padding-y N |
Vertical padding inside boxes (default: 2) |
--gap N |
Space between nodes (default: 4). Use 1 or 2 for compact diagrams |
--arrow-style STYLE |
Arrowheads: pointer (default, ► ◄ ▼ ▲), triangle (▶ ◀ ▼ ▲) or outline (▷ ◁ ▽ △). Also arrow_style= in render(), render_rich(), render_styled() and MermaidWidget |
--arrow-styles |
List the arrowhead styles |
--inline-edge-labels |
Attach flowchart labels directly to their edges |
--width N |
Max output width. Re-renders with smaller gap/padding, then wraps labels, if exceeded |
--strict-width |
Exit with status 2 instead of a warning when the output is still wider than --width |
--max-height ROWS |
Exit with status 2 when the output has more than ROWS lines (a check, not a fitting target) |
--no-auto-fit |
Disable automatic compaction when diagram exceeds terminal width |
--sharp-edges |
Sharp corners on edge turns instead of rounded |
-o FILE |
Write output to file instead of stdout |
--show-ids |
Show node IDs alongside labels for debugging (e.g. myId: My Label) |
--json TYPE |
Pipe JSON/tabular data and render as treemap, pie, mindmap, flowchart, or xychart |
--markdown |
Read a Markdown document and draw each mermaid code block in place; other text passes through. A block that cannot be drawn is kept as source, with a warning |
--claude-hook |
Run as a Claude Code MessageDisplay hook: read one batch of a reply as JSON on stdin and draw its mermaid blocks in place (see Claude Code) |
--tui |
Interactive TUI viewer (requires pip install termaid[tui]) |
--format FORMAT |
text (default) or styled-json: one JSON document of text chunks tagged with style keys (see Editor and tool integration) |
--diagnostics-format FORMAT |
text (default) or json: print each error and warning on stderr as one JSON line |
Render a Mermaid diagram as a plain text string. Auto-detects diagram type.
Render as a Rich Text object with colors. Requires pip install termaid[rich].
Render as theme-independent styled chunks, the same document that --format styled-json prints (see Editor and tool integration).
A Textual widget with a reactive source attribute. Requires pip install termaid[textual]. Updates live when you change the source property.
from termaid import MermaidWidget
class MyApp(App):
def compose(self):
yield MermaidWidget("graph LR\n A --> B")11 built-in themes for --theme / render_rich(). Run termaid --themes to list them.
Text themes (foreground colors only):
Solid themes (filled backgrounds with foreground colors):
pip install termaid[rich] # Colored terminal output
pip install termaid[textual] # Textual TUI widgetClaude often answers with Mermaid diagrams. With the termaid plugin, Claude Code shows them as drawings instead of code fences, and Claude still sees the Mermaid source. On Claude Code 2.1.275 or later, install it with one command:
/plugin install termaid --marketplace fasouto/termaid
On older versions, add the marketplace first with /plugin marketplace add fasouto/termaid
and then /plugin install termaid@termaid. The plugin carries its own copy of termaid
and needs Python 3.9 or later as python3, which macOS and most Linux systems already
have. It adds:
- a
MessageDisplayhook that runs the plugin's bundled copy of termaid withpython3on each reply and draws its mermaid blocks in place. It only changes what you see. The transcript keeps the source, and ifpython3is missing or termaid fails, the hook does nothing and Claude Code shows the original text. - a
/termaid:mermaidskill for explicit requests, such as rendering a file or the diagrams in a PR description.
Without the plugin, the same hook works from .claude/settings.json:
{
"hooks": {
"MessageDisplay": [
{ "hooks": [ { "type": "command", "command": "termaid --claude-hook", "timeout": 10 } ] }
]
}
}For headless use, pipe the reply through termaid --markdown:
claude -p "draw the login flow as a mermaid sequence diagram" | termaid --markdown
Editor plugins and other tools that run termaid as a subprocess can ask for structured output instead of terminal text.
--format styled-json prints the diagram as one line of JSON. Each line of the
diagram is a list of chunks, and each chunk is a run of text with one style key.
For graph LR; A-->B with --padding-x 1 --padding-y 0, wrapped here at each
diagram line:
{"version":1,"lines":[[{"text":"┌───┐","style":"node"},{"text":" ","style":"default"},{"text":"┌───┐","style":"node"}],
[{"text":"│","style":"node"},{"text":" ","style":"default"},{"text":"A","style":"label"},{"text":" ","style":"default"},{"text":"├","style":"node"},{"text":"───","style":"edge"},{"text":"►","style":"arrow"},{"text":"│","style":"node"},{"text":" ","style":"default"},{"text":"B","style":"label"},{"text":" ","style":"default"},{"text":"│","style":"node"}],
[{"text":"└───┘","style":"node"},{"text":" ","style":"default"},{"text":"└───┘","style":"node"}]]}
Joining the chunk texts of each line gives exactly the text output, fitted to
--width the same way. The tool picks the colors. Style keys name the role of
the cells (node, edge, arrow, label, edge_label, subgraph,
section:N, and so on). Flowcharts also carry class:<name>,
nodestyle:<id> and linkstyle:<index>, so a tool can apply the diagram's own
classDef, style and linkStyle colors. termaid ignores --theme here and
rejects --tui, --markdown and --claude-hook together with this format.
--diagnostics-format json prints each error and warning on stderr as one JSON
line, with a stable code to branch on:
$ termaid --diagnostics-format json --width 0 diagram.mmd
{"version": 1, "severity": "error", "code": "invalid_arguments", "message": "--width must be positive.", "details": {}, "exit_code": 2}
Codes: input_missing, input_empty, invalid_arguments,
input_conversion_failed, dependency_missing, render_failed,
render_empty, output_write_failed, width_exceeded, height_exceeded.
With --width alone, width_exceeded is a warning with status 0 and the
actual and target widths in details. With --strict-width it is an error.
- Layout engine is approximate. Node positioning uses a grid-based barycenter heuristic. Very dense graphs may still produce some edge crossings.
- Grid edge routing. Edges are horizontal and vertical segments found by A* search on a grid. Two edges share a line only when they share a node. Otherwise they cross, and the gap between layers grows to keep them apart, which can leave tall empty gaps in dense diagrams.
- Wide diagrams. The CLI auto-compacts when the diagram exceeds terminal width, and wraps labels at word boundaries when spacing alone cannot fit it. For very wide LR chains, use
--width N,--gap 1, or pipe throughless -S.
See the examples page for rendered examples of all 18 supported diagram types.
Rendering Mermaid usually takes a browser or an external service. termaid draws diagrams as text, so they work over SSH, in CI logs, in TUI apps, and anywhere Python runs. I built it because the other text renderers, mermaid-ascii (Go) and beautiful-mermaid (TypeScript), have no Python library to import.
Inspired by mermaid-ascii by Alexander Grooff and beautiful-mermaid by Lukilabs.
MIT































