Ghost: Lazy Loading Mermaid Rendering Script

Ghost: Lazy Loading Mermaid Rendering Script

Recently, I started trying out Ghost for publishing posts. Having been an Obsidian user for some time now, I have learned to truly appreciate Mermaid for diagramming as Obsidian has out of the box support for it. Ghost on the other hand, does not support rendering of Mermaid code blocks without some additional effort.

Researching on how to get it setup, I came across the Ghost Mermaid JS post by Samuel Hierholzer. The post details how to get it setup, and it works really well. The summary of the steps required are as follows.

  1. Inject loading of Mermaid script in the site header.
  2. Wrap all mermaid code blocks with <div class="mermaid"> and initialise Mermaid using a site footer script.

And this is it. However, this got me wondering if we can do this site wide but also get the benefit of lazy loading the script only when a mermaid code block is present in the rendered post.

The answer seems to be, yes. I have managed to get the injected code down to a single block in the site footer. As described in the above article, you can go to Settings -> Code Injection and then add the following snippet into the site footer.

 <script>  
  document.addEventListener("DOMContentLoaded", function () {  
    const mermaidElementsExist = document.querySelector("pre code.language-mermaid, div.mermaid");  
  
    if (mermaidElementsExist) {  
      var mElements = document.getElementsByClassName("language-mermaid");  
      while (mElements.length > 0) {  
        var element = mElements[0];  
        let text = element.innerHTML;  
        element.outerHTML = '<div class="mermaid">' + text + "</div>";  
      }  
  
      import("https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.esm.min.mjs")  
        .then((module) => {  
          try {  
            const mermaid = module.default;  
  
            if (mermaid && typeof mermaid.initialize === "function") {  
              mermaid.initialize({  
                startOnLoad: true,
              });  
            } else {  
              // Fallback or alternative check if it's a named export directly  
              if (typeof module.initialize === "function") {  
                module.initialize({ startOnLoad: true });  
                console.log("Mermaid initialization attempted via module.initialize().");  
              }  
            }  
          } catch (initError) {  
            console.error("Error during Mermaid initialization:", initError);  
          }  
        })  
        .catch((error) => {  
          console.error("Failed to dynamically import Mermaid library:", error);  
        });  
    }  
  });  
</script>
ℹ️
The script used above refers to mermaid@latest which redirects to the latest version of the script. You can consider specifying either a major or specific version.

Once you have this added, all posts with a mermaid code block would auto-render. Like the block below as this site already uses the above script.

  gitGraph
   commit id: "post"
   commit id: "Another post"
   branch mermaid
   checkout mermaid
   commit id: "Add header"
   commit id: "Add footer"
   checkout main
   merge mermaid
   commit id: "Stonks"

Example from Ghost Mermaid JS post by Samuel Hierholzer

This is not a make or break optimisation, but one that was intrigued by curiosity and encouraged by laziness. Hope this comes in handy for someone else too. Happy publishing!

Read more