πŸ”Œ

gfm-render

πŸ”ŒPlugin🧩 Mod

A Claude Code mod that formats GitHub-style Markdown inside Claude's replies in the terminal. Warning boxes, checklists, crossed-out text and diagrams appear formatted instead of as raw text.

briangtn/claude-gfm-render24Added Nov 12, 2026

Installation

$git clone https://github.com/briangtn/claude-gfm-render.git
$claude --plugin-dir ./claude-gfm-render

This mod is not in a marketplace: clone the repo and load its folder. Run these in your terminal.

Permissions to check

  • Runs programs

Also: Interface Β· Own data

Raw check output (Claude Code 2.1.322)

hooks: ui.render{component=AssistantMessage}

calls: $.process.run (via mermaidSvg), $.state.get, $.state.set, $.ui.resolve

mod

reach validates

claude-gfm-render

Alerts, task lists, strikethrough and Mermaid diagrams, drawn inside Claude Code's replies.

Claude Code Mod Tests License: MIT

Install Β· Before / after Β· What it handles Β· How it works


Claude writes GitHub Flavored Markdown all day: > [!WARNING] callouts, - [ ] checklists, ```mermaid diagrams. The terminal shows them as raw text. This mod draws them, without touching the message itself (ctrl+o still shows the original).

Install

git clone https://github.com/briangtn/claude-gfm-render.git ~/perso/claude-gfm-render
claude --plugin-dir ~/perso/claude-gfm-render

To load it in every session, terminal and desktop app alike, add it to ~/.claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_PLUGIN_DIRS": "~/perso/claude-gfm-render"
  }
}

[!NOTE] Mods (function hooks) are an early-access API of Claude Code and may change between releases. Built and tested on Claude Code 2.1.286 and 2.1.287. No build step: the mod is plain files, and the folder is watched, so an edit reloads it in the running session.

Claude writes GFM on its own

Drawing GFM is half the job: Claude also has to write it. The mod ships GFM.md, a short note on what the transcript can draw (alerts, task lists, strikethrough, the Mermaid types that render), which a SessionStart hook adds to Claude's context at startup, after /clear and after a compaction. No CLAUDE.md to edit.

To turn it off and keep only the rendering, in ~/.claude/settings.json:

{
  "pluginConfigs": {
    "gfm-render": { "options": { "promptHint": false } }
  }
}

Before / after

Real claude sessions (100 columns, captured with tmux); before is the same reply without the mod.

On desktop, VS Code and mobile

Alerts get a little more room, captured in the Claude desktop app (dark theme):

Mermaid becomes a real SVG that follows the light or dark scheme. Below, the mod's own output rendered by Chrome (not a capture of the app):

LightDark
Sequence SVG, lightSequence SVG, dark

What it handles

MarkdownTerminalDesktop Β· VS Code Β· mobile
πŸ’¬Alerts > [!NOTE] [!TIP] [!IMPORTANT] [!WARNING] [!CAUTION], any markdown insideβœ… colored boxβœ… colored box
β˜‘οΈTask lists - [ ] / - [x] (also *, +, 1.)βœ… ☐ / β˜‘βž– native
SStrikethrough ~~text~~βœ…βž– native
πŸ”€Mermaid flowchart / graphβœ… Unicode artβœ… SVG
🧭Mermaid sequence, state, class, ER, xychartβœ… Unicode artβœ… SVG
πŸ”’Code fences and inline codenever rewrittennever rewritten
πŸ“Everything else (headings, tables, links, emphasis…)nativenative
CaseWhat you get
Mermaid gantt, pie, mindmap, gitGraph, journey, timeline, quadrantChart, sankey, C4…the code block, as written
Mermaid with a syntax error, or wider than the terminal even with tighter spacingthe code block
Mermaid on desktop without node on the session's PATHthe Unicode art in a code block
Single-line Mermaid (graph TD; A-->B)may fail to parse: write one statement per line
An alert nested in a list item or in another blockquote (> > [!NOTE])a plain blockquote
A reply still streaming inthe native rendering until the alert or the closing ``` arrives
A single reply block over 10,000 charactersthe native rendering of that block
Footnotes, emoji shortcodes, #123 / @user autolinks, $math$, raw HTMLleft to the native renderer
Your own messages and tool outputuntouched: only Claude's replies are drawn

Known glitch: in the terminal, an edge label leaving a {decision} node can show a stray β”œ (beautiful-mermaid's ASCII layout, visible in the flowchart above).

How it works

The mod hooks ui.render on AssistantMessage: every text block of a reply goes through it, gets split into markdown, alert and Mermaid segments, and comes back as a tree the surface draws. A block with nothing to draw goes to the native renderer untouched.

graph LR
  A[Reply block] --> B{GFM inside?}
  B -->|no| N[Native renderer]
  B -->|alert| C[Colored box]
  B -->|mermaid| D{Surface}
  D -->|terminal| E[Unicode art, in process]
  D -->|desktop| F[SVG via node]

Mermaid is rendered by beautiful-mermaid, split in two because a hooks module may not import a file over 1 MiB and has no eval, while the ELK layout engine the SVG needs weighs 1.6 MB:

  • Terminal: hooks/vendor/mermaid-ascii.js, the ASCII renderer alone (84 KB), imported by the mod.
  • Desktop / VS Code / mobile: renderer/svg.mjs run by node, once per diagram, then cached.

Develop

npm test                # claude plugin test .
npm run validate        # claude plugin validate .
npm run build:vendor    # rebuild both Mermaid bundles with bun
FileRole
hooks/register.tsxthe ui.render hook and the SVG process call
hooks/gfm.tssplits a reply into blocks; task-list and strikethrough rewrites
hooks/mermaid.tsUnicode art sized to the terminal, SVG theming
hooks/gfm.test.tsxtests, run on every surface
renderer/svg.mjsMermaid on stdin, SVG on stdout
GFM.mdwhat Claude is told it can write, loaded by the SessionStart hook in hooks/hooks.json

Issues and PRs welcome, especially screenshots from other terminals and the desktop app's light theme.

License

MIT. Vendored bundles keep their own licenses: beautiful-mermaid (MIT) and elkjs (EPL-2.0), in renderer/.