Add Mermaid Diagrams to a Hugo Blog

A while back I wrote about adding ECharts to this blog with Hugo Shortcodes. But for day-to-day technical writing, what I usually need is flowcharts and sequence diagrams — the “text-is-the-picture” kind. That’s exactly what Mermaid does: describe a diagram in a few lines of text, no canvas dragging, no pasting screenshots.

And Hugo now has a cleaner approach than Shortcodes — the code block render hook. Since Hugo v0.123 I can override how code blocks render, intercepting ```mermaid fenced blocks and turning them into renderable diagram containers at build time.

1. Create the render hook

Create layouts/_default/_markup/render-codeblock.html in the project root:

{{ $lang := .Type | default "plain" }}
{{ if eq $lang "mermaid" }}
<pre class="mermaid" role="img" aria-label="Mermaid diagram">{{ .Inner }}</pre>
{{ if not (.Page.Store.Get "mermaidLoaded") }}
{{ .Page.Store.Set "mermaidLoaded" true }}
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({
  startOnLoad: false,
  theme: 'base',
  themeVariables: {
    background: '#1d1f21',
    primaryColor: '#282c34',
    primaryBorderColor: '#3e4147',
    primaryTextColor: '#c9cacc',
    lineColor: '#5a5f66',
    edgeLabelBackground: '#1d1f21'
  }
});
mermaid.run({ querySelector: 'pre.mermaid' });
</script>
{{ end }}
{{ else }}
{{ highlight .Inner .Type }}
{{ end }}

What this template does is simple:

  • When the code block language is mermaid, it wraps the content in <pre class="mermaid">, loads mermaid v11 (ESM build) and calls mermaid.run() to render an SVG in the browser
  • The .Page.Store guard emits the <script> only once per page, so multiple diagrams still load mermaid a single time
  • role="img" and aria-label on the <pre> help screen readers treat it as an image
  • themeVariables tunes the palette to match this blog’s dark background; I tweak the hex values to fit my own theme
  • Every other language keeps using Hugo’s built-in highlight logic, unaffected

Note: Hugo already HTML-escapes .Inner for code blocks, so the template outputs it directly — the browser’s textContent decodes it back to the original text. I escaped it a second time once and mermaid parsed the literal &lt; entities, throwing Syntax error in text. Now I only output .Inner directly.

2. Use it in articles

In Markdown, just write a mermaid fenced code block:

```mermaid
graph TB
    A[Start] --> B{Does it work?}
    B -- Yes --> C[Rendered!]
    B -- No --> D[Check the console]
```

No Shortcodes, no front matter declaration. It just works at build time.

3. Live test

If all the diagrams below show as real graphics (not code text), the Mermaid support is live.

Flowchart

Sequence diagram

Class diagram

Gantt chart

Pie chart

4. Notes

  • Code block render hooks require Hugo v0.123+
  • Mermaid is loaded on demand from a CDN — only pages that actually contain a mermaid block pull it in
  • Diagrams are rendered to SVG client-side; crawlers still see the readable raw text, so it’s SEO-friendly

5. Preventing future diagram bugs

Diagrams render in the browser, so a broken diagram never fails the build — it just shows Syntax error in text on the page. I hit this once: I applied an extra htmlEscape to .Inner in the render hook, turning the already-escaped --> into --&gt;. The browser’s textContent decoded back to a literal &gt;, and mermaid refused to parse it.

So I turned the check into a skill — mermaid-lint. It validates every mermaid block under content/ with the real mermaid parser, plus two static checks: literal HTML entities in the content (double-escaping) and a first line that isn’t a valid diagram type. The core is scripts/lint-mermaid.mjs:

node scripts/lint-mermaid.mjs

All green means every diagram renders; a broken one prints which article and which block is wrong and why, and the exit code is non-zero — easy to gate in CI.

One rule to follow: write raw characters in the Markdown source (-->, "label"), never HTML entities like &gt; or &#34;.


If the flowchart, sequence diagram, class diagram, gantt and pie charts above all rendered as real graphics, everything is working. To add this capability to my Hugo blog, all I needed was to copy that one template file.

Article Link:

https://time-friend.com/en/archive/make-your-hugo-blog-support-mermaid-diagrams/

# Related Articles