Getting Mermaid Diagrams Working in Hugo
Let’s be honest: adding Mermaid diagrams to your Hugo site should be straightforward, but it’s often a frustrating experience. Most tutorials gloss over the critical theme-specific modifications required, leaving you with broken diagrams and cryptic errors.
After spending way too many hours banging my head against this particular wall with the m10c theme, I’m documenting the actual working solution so you don’t have to suffer through the same pain.
An Intro to Mermaid Through Examples
If you’re coming to this blog post and don’t know what Mermaid diagrams are, they’re a way to create diagrams through code. Here are several visuals to give you an idea of what you can do with Mermaid.
Sankey Diagrams
sankey-beta Traffic Sources, Social Media, 20 Traffic Sources, Direct, 40 Traffic Sources, Search, 40 Social Media, Facebook, 8 Social Media, Twitter, 7 Social Media, LinkedIn, 5 Direct, Homepage, 25 Direct, Blog, 15 Search, Homepage, 20 Search, Blog, 15 Search, Products, 5
```mermaid
sankey-beta
Traffic Sources, Social Media, 20
Traffic Sources, Direct, 40
Traffic Sources, Search, 40
Social Media, Facebook, 8
Social Media, Twitter, 7
Social Media, LinkedIn, 5
Direct, Homepage, 25
Direct, Blog, 15
Search, Homepage, 20
Search, Blog, 15
Search, Products, 5
```
Sequence Diagrams
sequenceDiagram participant Client participant API participant DB Client->>+API: GET /users API->>+DB: SELECT * FROM users DB-->>-API: Return user data API-->>-Client: 200 OK (JSON) Note over Client,API: Normal flow Client->>+API: GET /invalid API-->>-Client: 404 Not Found
```mermaid
sequenceDiagram
participant Client
participant API
participant DB
Client->>+API: GET /users
API->>+DB: SELECT * FROM users
DB-->>-API: Return user data
API-->>-Client: 200 OK (JSON)
Note over Client,API: Normal flow
Client->>+API: GET /invalid
API-->>-Client: 404 Not Found
```
Class Diagrams
classDiagram class Animal { +String name +int age +makeSound() } class Dog { +String breed +bark() } class Cat { +bool hasNineLives +meow() } Animal <|-- Dog Animal <|-- Cat
```mermaid
classDiagram
class Animal {
+String name
+int age
+makeSound()
}
class Dog {
+String breed
+bark()
}
class Cat {
+bool hasNineLives
+meow()
}
Animal <|-- Dog
Animal <|-- Cat
```
State Diagrams
stateDiagram-v2 [*] --> Pending Pending --> Processing: Payment received Processing --> Shipped: Item in stock Processing --> Backordered: Out of stock Backordered --> Shipped: Restock Shipped --> Delivered Shipped --> Returned: Customer returns Delivered --> [*] Returned --> [*]
```mermaid
stateDiagram-v2
[*] --> Pending
Pending --> Processing: Payment received
Processing --> Shipped: Item in stock
Processing --> Backordered: Out of stock
Backordered --> Shipped: Restock
Shipped --> Delivered
Shipped --> Returned: Customer returns
Delivered --> [*]
Returned --> [*]
```
Entity Relationship Diagrams
erDiagram USER ||--o{ POST : writes USER { int id string email string name } POST ||--o{ COMMENT : has POST { int id string title string content date created_at } COMMENT { int id string content int user_id }
```mermaid
erDiagram
USER ||--o{ POST : writes
USER {
int id
string email
string name
}
POST ||--o{ COMMENT : has
POST {
int id
string title
string content
date created_at
}
COMMENT {
int id
string content
int user_id
}
```
Gantt Chart Diagrams
gantt title Project Timeline dateFormat YYYY-MM-DD section Planning Requirements :a1, 2024-01-01, 30d Design :after a1, 20d section Development Coding :2024-02-15, 45d Testing :2024-03-01, 30d section Launch Deployment :2024-04-01, 10d
```mermaid
gantt
title Project Timeline
dateFormat YYYY-MM-DD
section Planning
Requirements :a1, 2024-01-01, 30d
Design :after a1, 20d
section Development
Coding :2024-02-15, 45d
Testing :2024-03-01, 30d
section Launch
Deployment :2024-04-01, 10d
```
Nice, huh? Diagrams make it easier for some readers to understand concepts. Through the work in this blog, I was able to implement the same Mermaid codeblocks as in my Obsidian notes.
The Problem with Standard Hugo Mermaid Tutorials
The official Hugo documentation provides a basic implementation that works great… if you’re using the default theme with no customizations. But let’s be real - who does that?
Most of us are using custom themes (like m10c in my case), and that’s where things break down. The standard implementation doesn’t account for how themes structure their templates and load JavaScript.
The Solution: Theme-Specific Implementation
Here’s my working implementation for the m10c theme, with explanations of why each part matters:
Step 1: Create the Base Template Override
First, you need to override your theme’s base template. For m10c, that means:
- Identify the original template:
themes/m10c/layouts/_default/baseof.html
- Create your override at:
layouts/_default/baseof.html
- Copy the content from the theme’s template to your override
Step 2: Add Mermaid Support to the Base Template
Next, modify your override to include the Mermaid library. Add this code just before the closing </body>
tag:
<!-- Add Mermaid support - must be placed before closing body tag -->
{{ if .Store.Get "hasMermaid" }}
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
mermaid.initialize({
startOnLoad: true,
theme: 'dark', // Options: default, forest, dark, neutral
securityLevel: 'loose',
// Add custom theme properties
themeVariables: {
// Primary colors
primaryColor: '#BB2528',
primaryTextColor: '#fff',
primaryBorderColor: '#7C0000',
// Secondary colors
secondaryColor: '#006100',
secondaryTextColor: '#fff',
secondaryBorderColor: '#004d00',
// Other colors as needed
mainBkg: '#1f2020',
textColor: '#ddd',
lineColor: '#999',
// Specific diagram colors
nodeBorder: '#777',
titleColor: '#F8F8F8'
}
});
</script>
{{ end }}
This code checks if the page has Mermaid content (using Hugo’s Store) and only loads the Mermaid library when needed. The custom theme variables ensure your diagrams match your site’s dark theme.
Step 3: Create the Render Hook
Now create a file at layouts/_default/_markup/render-codeblock-mermaid.html
with the following content:
<pre class="mermaid">
{{- .Inner | htmlEscape | safeHTML }}
</pre>
{{ .Page.Store.Set "hasMermaid" true }}
This render hook does two critical things:
- Wraps your Mermaid code in the correct HTML structure
- Sets a flag in the page’s Store so the base template knows to load the Mermaid library
How This Actually Works
Let me explain the process flow with a Mermaid diagram:
flowchart TD A[Write Markdown with Mermaid Code] --> B[Hugo Processes Markdown] B --> C[Render Hook Processes Mermaid Blocks] C --> D[Sets hasMermaid Flag in Page Store] D --> E[Base Template Checks for hasMermaid Flag] E --> F[Mermaid Library Loads if Flag is True] F --> G[Mermaid Library Renders Diagrams] style A fill:#222,stroke:#BB2528,color:#ddd style G fill:#006100,stroke:#004d00,color:#ddd
The key insight here is that the render hook and base template work together through Hugo’s Store mechanism. Without either piece, the system breaks down.
Using Mermaid in Your Content
With this implementation in place, you can now add Mermaid diagrams to your Markdown content like this:
```mermaid
flowchart LR
A[Start] --> B{Decision}
B -->|Yes| C[Action 1]
B -->|No| D[Action 2]
```
mermaid
language identifier for your code blocks, not just triple backticks.Why Your Theme Matters
Different Hugo themes structure their templates differently. The approach I’ve outlined works for m10c, but you might need to adjust it for other themes. The general principles remain the same:
- Override the base template that controls your HTML output
- Add conditional loading of the Mermaid library based on content
- Create a render hook that flags pages containing Mermaid code
Common Pitfalls to Avoid
Here are some issues I encountered that aren’t mentioned in most tutorials:
- Missing the theme override: Just adding the render hook isn’t enough
- Incorrect script loading: Modern Mermaid uses ES modules, requiring the type=“module” attribute
- Theme incompatibility: Dark-themed sites need custom Mermaid styling
- Resource loading order: The Mermaid library must load after your content is ready
Conclusion
Getting Mermaid diagrams working in Hugo requires understanding how your specific theme handles templates and JavaScript. The solution I’ve outlined for m10c addresses these theme-specific concerns and provides a robust implementation that works reliably.
By overriding the appropriate templates and leveraging Hugo’s Store mechanism, you can seamlessly integrate Mermaid diagrams into your content without fighting with your theme.