Hugo Mermaid shortcode

Hugo Mermaid shortcode

A shortcode which allows to easily integrate Mermaid diagrams in your Hugo content. Mermaid is a JavaScript library which can draw various diagrams (flowchart, sequence diagram, class diagram, state diagram, …), it is well documented and there are plenty examples available.

This article was written for Hugo 0.74.

The mermaid library is used to draw diagrams. At the time of writing 8.5.2.

Mermaid will generate the diagrams on first page load BUT diagrams cannot not be embedded in a display: none; element (will cause errors).

The diagrams are generated on the fly client side, this might cause page jumps. Alternatively one can generate the mermaid images once and include them as svg files which saves compute time on the client in exchange for more bandwidth. If you want to pre-generate diagrams you can also use diagrams.net instead (online/desktop and free).

Usage

There are no options to speak of, place the mermaid syntax in the shortcode an off you go.

option   effect   optionaldefault
classadds additional CSS class(es)yes

Example

{{< mermaid >}}
graph TD
A[Client] -->|tcp_123| B(Load Balancer)
B -->|tcp_456| C[Server1]
B -->|tcp_456| D[Server2]
{{< /mermaid >}}

graph TD A[Client] -->|tcp_123| B(Load Balancer) B -->|tcp_456| C[Server1] B -->|tcp_456| D[Server2]

Sequence diagram

{{< mermaid [class="text-center"] >}}
sequenceDiagram
    Alice->>Bob: Hello Bob, how are you?
    alt is sick
        Bob->>Alice: Not so good :(
    else is well
        Bob->>Alice: Feeling fresh like a daisy
    end
    opt Extra response
        Bob->>Alice: Thanks for asking
    end
{{< /mermaid >}}

<—>

sequenceDiagram Alice->>Bob: Hello Bob, how are you? alt is sick Bob->>Alice: Not so good :( else is well Bob->>Alice: Feeling fresh like a daisy end opt Extra response Bob->>Alice: Thanks for asking end

Flowchart

{{< mermaid [class="text-center"] >}}
graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
{{< /mermaid >}}

<—>

graph TD A[Hard] -->|Text| B(Round) B --> C{Decision} C -->|One| D[Result 1] C -->|Two| E[Result 2]

Gantt diagram

{{< mermaid [class="text-center"] >}}
gantt
    title A Gantt Diagram
    dateFormat  YYYY-MM-DD
    section Section
    A task           :a1, 2014-01-01, 30d
    Another task     :after a1  , 20d
    section Another
    Task in sec      :2014-01-12  , 12d
    another task      : 24d
{{< /mermaid >}}

gantt title A Gantt Diagram dateFormat YYYY-MM-DD section Section A task :a1, 2014-01-01, 30d Another task :after a1 , 20d section Another Task in sec :2014-01-12 , 12d another task : 24d

Class diagram

{{< mermaid [class="text-center"] >}}
classDiagram
  Animal <|-- Duck
  Animal <|-- Fish
  Animal <|-- Zebra
  Animal : +int age
  Animal : +String gender
  Animal: +isMammal()
  Animal: +mate()
  class Duck{
      +String beakColor
      +swim()
      +quack()
  }
  class Fish{
      -int sizeInFeet
      -canEat()
  }
  class Zebra{
      +bool is_wild
      +run()
  }
{{< /mermaid >}}

<—>

classDiagram Animal <|-- Duck Animal <|-- Fish Animal <|-- Zebra Animal : +int age Animal : +String gender Animal: +isMammal() Animal: +mate() class Duck{ +String beakColor +swim() +quack() } class Fish{ -int sizeInFeet -canEat() } class Zebra{ +bool is_wild +run() }

State diagram

{{< mermaid [class="text-center"] >}}
stateDiagram
  [*] --> Still
  Still --> [*]
  Still --> Moving
  Moving --> Still
  Moving --> Crash
  Crash --> [*]
{{< /mermaid >}}

<—>

stateDiagram [*] --> Still Still --> [*] Still --> Moving Moving --> Still Moving --> Crash Crash --> [*]

Pie chart

{{< mermaid [class="text-center"] >}}
pie
  title Key elements in Product X
  "Calcium" : 42.96
  "Potassium" : 50.05
  "Magnesium" : 10.01
  "Iron" :  5
{{< /mermaid >}}

<—>

pie title Key elements in Product X "Calcium" : 42.96 "Potassium" : 50.05 "Magnesium" : 10.01 "Iron" : 5

Code

Adding mermaid support is very easy, just load the Javascript and add an element with the mermaid class.
Because mermaid is fairly heavy it is only included if the shortcode is used at least once on a page. This is done by setting the scratch variable.

1
2
3
4
5
6
7
{{ if not (.Page.Scratch.Get "mermaid") }}
<!-- Include mermaid only first time -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@8.7.0/dist/mermaid.min.js" crossorigin="anonymous"
        onerror="cdnFB(this, '{{ "js/thirdParty/mermaid.min.js" | relURL }}')"></script>
{{ .Page.Scratch.Set "mermaid" true }}
{{ end }}
<p class="mermaid{{ with (.Get "class") }} {{ . }}{{ end }}">{{- .Inner -}}</p>

CDN are useful to save yourself bandwidth, however if they go down1 your site will break. In that case you would want to redirect to your own hosted copy instead so your site remains online.
This little javascript snippet will do just that, you have to wire it to the onerror event (see example above). Add a copy of the file to your static folder in Hugo.

1
2
3
4
5
// Fallback to load another version of the resource if the first one failed
function cdnFB(element, source) {
    element.onerror = null;
    element.href = source;
}


  1. It has happend that CDN’s go down for various reasons. Misconfigured network or general server issues. ↩︎

Noticed an error in this post? Corrections are appreciated.

© Nelis Oostens