• An Intro to Mermaid Through Examples
    • Sankey Diagrams
    • Sequence Diagrams
    • Class Diagrams
    • State Diagrams
    • Entity Relationship Diagrams
    • Gantt Chart Diagrams
  • The Problem with Standard Hugo Mermaid Tutorials
  • The Solution: Theme-Specific Implementation
    • Step 1: Create the Base Template Override
    • Step 2: Add Mermaid Support to the Base Template
    • Step 3: Create the Render Hook
  • How This Actually Works
  • Using Mermaid in Your Content
  • Why Your Theme Matters
  • Common Pitfalls to Avoid
  • Conclusion
March 1, 2025 · 7 min read

Getting Mermaid Diagrams Working in Hugo

Hugo Mermaid Blogging
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.

💡 TIP
The key insight: You need to override your theme’s base template, not just add the render hook.

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:

  1. Identify the original template: themes/m10c/layouts/_default/baseof.html
  2. Create your override at: layouts/_default/baseof.html
  3. 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:

  1. Wraps your Mermaid code in the correct HTML structure
  2. 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]
```
💡 TIP
Always use the explicit 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:

  1. Override the base template that controls your HTML output
  2. Add conditional loading of the Mermaid library based on content
  3. 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:

  1. Missing the theme override: Just adding the render hook isn’t enough
  2. Incorrect script loading: Modern Mermaid uses ES modules, requiring the type=“module” attribute
  3. Theme incompatibility: Dark-themed sites need custom Mermaid styling
  4. 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.