Mermaid Diagrams on GitHub Pages — A Rendering Test
A Mermaid diagram in a Markdown file renders on github.com and shows up as a block of source text on the same repository’s GitHub Pages site. The two are different renderers, and only one of them knows what Mermaid is.
- github.com renders
.mdfiles with its own pipeline, which has had Mermaid support since February 2022 and currently runs Mermaid 11. - GitHub Pages builds the site with Jekyll. Kramdown turns the fence into
<pre><code class="language-mermaid">and stops there. Nothing on the page loads Mermaid, so the browser displays the source.
This page is the test. It loads Mermaid only when a post declares isMermaid: true, the same way the site already loads MathJax for isMath: true.
Flowchart
flowchart LR
A["Markdown file<br/>```mermaid fence"] --> B{"Which renderer?"}
B -->|"github.com"| C["Rendered as a diagram<br/>Mermaid is built in"]
B -->|"GitHub Pages"| D["Rendered as a code block<br/>kramdown leaves it alone"]
D --> E["Unless the layout<br/>loads Mermaid itself"]
E --> C
classDef ok fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20
classDef no fill:#FDEEEE,stroke:#B54A4A,color:#6B2020
class C ok
class D no
Sequence diagram
sequenceDiagram
autonumber
participant V as Visitor
participant P as GitHub Pages
participant J as jsDelivr CDN
V->>P: GET /2026/09/29/mermaid-on-github-pages-test/
P-->>V: HTML, with the fence as a code block
V->>J: import mermaid@11 (only if isMermaid is set)
J-->>V: mermaid.esm.min.mjs
V->>V: replace each code block with div.mermaid
V->>V: mermaid.run() draws the SVG
State diagram
stateDiagram-v2
[*] --> Source: fence written
Source --> CodeBlock: kramdown builds the page
CodeBlock --> Diagram: mermaid.run() in the browser
Diagram --> Diagram: re-render on theme change
Diagram --> [*]
What the include does
Three details matter, and each one comes from a difference between Mermaid’s expectations and what Jekyll produces.
- Kramdown wraps the source in two elements. Mermaid wants the diagram source inside the element it is pointed at, so each
code.language-mermaidis read withtextContent, which decodes HTML entities and drops any syntax-highlighting spans, and the wrapper is replaced by a<div class="mermaid">. - Mermaid marks what it has already drawn. It sets
data-processedon a node and skips it afterwards, so a second render needs the original source back. The include keeps it indata-sourceand restores it before re-running. - The site has a light and a dark theme. A
MutationObserverondata-themere-initialises Mermaid with itsdarkordefaulttheme and redraws, so the diagrams follow the page instead of staying bright on a dark background.
The cost, and when to pay it
Loading a diagram library on a page that has no diagram is waste, which is why the script is gated on a front-matter flag rather than added to every page. The Mermaid ESM bundle is not small, and it is fetched from a CDN, so a reader without JavaScript, or behind a CDN block, sees the fence as source text. That is the honest failure mode for this approach: the page stays readable, the diagram does not appear.
An alternative avoids the runtime entirely: render the diagrams to SVG at build time with the Mermaid CLI and commit the images. It costs a build step and the diagrams stop being editable in the Markdown, and in exchange nothing is fetched at page load and the result renders everywhere, including in RSS readers.