Skip to main content

Mermaid Diagrams

Markdown-OS provides full support for Mermaid diagrams with interactive pan/zoom controls, fullscreen viewing, and theme synchronization. Diagrams are rendered using Mermaid.js with svg-pan-zoom for interactivity.

Creating Diagrams

Mermaid diagrams are created using fenced code blocks with the mermaid language identifier:
```mermaid
graph TD
  A[Start] --> B{Decision}
  B -->|Yes| C[Action 1]
  B -->|No| D[Action 2]
  C --> E[End]
  D --> E
```
The editor automatically detects language-mermaid or lang-mermaid code blocks and renders them as interactive diagrams.

Diagram Types

Mermaid supports many diagram types:
  • Flowcharts: graph TD, graph LR
  • Sequence Diagrams: sequenceDiagram
  • Class Diagrams: classDiagram
  • State Diagrams: stateDiagram-v2
  • Entity Relationship: erDiagram
  • Gantt Charts: gantt
  • Pie Charts: pie
  • Git Graphs: gitGraph

Interactive Controls

Each rendered diagram includes interactive controls:

Inline Toolbar

1

Edit Button

Opens a modal editor to modify the diagram source code
2

Fullscreen Button

Expands the diagram to fullscreen with enhanced pan/zoom controls

Zoom Controls

Every diagram has zoom controls in the bottom-right corner:
  • + Button: Zoom in
  • Reset Icon: Reset view (fit and center)
  • − Button: Zoom out
let controls = container.querySelector(".mermaid-zoom-controls");
if (!controls) {
  controls = document.createElement("div");
  controls.className = "mermaid-zoom-controls";
  
  const zoomIn = document.createElement("button");
  zoomIn.addEventListener("click", () => {
    container._panZoomInstance?.zoomIn();
  });
  
  const reset = document.createElement("button");
  reset.addEventListener("click", () => {
    const pz = container._panZoomInstance;
    pz.resetZoom();
    pz.resetPan();
    pz.fit();
    pz.center();
  });
  
  const zoomOut = document.createElement("button");
  zoomOut.addEventListener("click", () => {
    container._panZoomInstance?.zoomOut();
  });
}
You can also pan diagrams by clicking and dragging, and zoom with the mouse wheel.

Pan & Zoom

Diagrams use svg-pan-zoom library for smooth interaction:
function applyZoomToDiagrams() {
  state.root.querySelectorAll(".mermaid-container .mermaid-canvas svg")
    .forEach((svgElement) => {
      const instance = window.svgPanZoom(svgElement, {
        controlIconsEnabled: false,
        zoomScaleSensitivity: 0.03,
        minZoom: 0.5,
        maxZoom: 20,
        fit: true,
        center: true,
      });
      container._panZoomInstance = instance;
    });
}

Pan/Zoom Features

  • Zoom Sensitivity: 0.03 for smooth, precise zooming
  • Zoom Range: 0.5x to 20x magnification
  • Auto-fit: Diagrams automatically fit within the container on load
  • Auto-center: Diagrams are centered after fitting

Fullscreen Mode

Click the fullscreen button to view diagrams in an expanded modal:
1

Open Fullscreen

Diagram is re-rendered at full viewport size with loading spinner
2

Enhanced Controls

Fullscreen mode has wider zoom range (0.2x to 40x) and full-viewport panning
3

Close

Press Escape or click the close button to return to the editor
async function openMermaidFullscreen(mermaidSource, fallbackSvg) {
  state.fullscreenRenderToken += 1;
  const renderToken = state.fullscreenRenderToken;
  
  // Show loading spinner
  content.innerHTML = '<div class="mermaid-fullscreen-loading">...</div>';
  
  // Render diagram
  const result = await window.mermaid.render(fullscreenId, mermaidSource);
  content.innerHTML = result.svg;
  
  // Apply pan/zoom with expanded limits
  state.fullscreenPanZoom = window.svgPanZoom(svgElement, {
    minZoom: 0.2,
    maxZoom: 40,
    fit: true,
    center: true,
  });
}
Fullscreen mode preserves editor scroll position and focus, restoring them when closed.

Theme Synchronization

Mermaid diagrams automatically adapt to the active editor theme:
const mermaidThemeByAppTheme = {
  light: "default",
  dark: "dark",
  dracula: "dark",
  "nord-light": "neutral",
  "nord-dark": "dark",
  lofi: "neutral",
};

Theme Initialization

function ensureMermaidInitialized() {
  const theme = currentMermaidTheme();
  if (state.mermaidInitialized && state.mermaidTheme === theme) {
    return;
  }
  
  window.mermaid.initialize({
    startOnLoad: false,
    securityLevel: "strict",
    theme,
    useMaxWidth: false,
  });
  state.mermaidInitialized = true;
  state.mermaidTheme = theme;
}
Changing the theme re-initializes Mermaid, which triggers re-rendering of all diagrams on the page.

Rendering Pipeline

Diagrams go through a multi-step rendering process:
1

Detection

Code blocks with language-mermaid are detected during markdown parsing
2

Container Creation

Each diagram is wrapped in a .mermaid-container with a .mermaid-canvas
3

Mermaid Rendering

mermaid.run() processes all .mermaid nodes and generates SVG output
4

SVG Sizing

Custom logic fixes SVG dimensions to maintain aspect ratio within max height
5

Pan/Zoom Setup

svg-pan-zoom is attached to enable interactive controls
6

Controls Addition

Edit, fullscreen, and zoom control buttons are added to the container
async function renderMermaidDiagrams() {
  ensureMermaidInitialized();
  
  // Wrap code blocks in mermaid containers
  mermaidCodeBlocks.forEach((codeElement) => {
    const mermaidContainer = document.createElement("div");
    mermaidContainer.className = "mermaid-container";
    mermaidContainer.dataset.mermaidSource = sourceContent;
    preElement.replaceWith(mermaidContainer);
  });
  
  // Render all diagrams
  await window.mermaid.run({ nodes: mermaidNodes });
  
  // Fix dimensions and add interactivity
  fixMermaidSvgDimensions();
  applyZoomToDiagrams();
  
  // Add controls
  state.root.querySelectorAll(".mermaid-container").forEach((container) => {
    addMermaidControls(container);
  });
}

SVG Dimension Handling

Diagrams are sized intelligently to fit within constraints:
function fixMermaidSvgDimensions() {
  state.root.querySelectorAll(".mermaid-container .mermaid-canvas svg")
    .forEach((svg) => {
      const bbox = svg.getBBox();
      const containerWidth = canvas.clientWidth;
      const maxHeightPx = parseMermaidMaxHeight(maxHeightValue);
      const naturalHeight = containerWidth * (bbox.height / bbox.width);
      
      if (naturalHeight <= availableHeight) {
        // Fits naturally
        svg.style.width = "100%";
        svg.style.height = `${Math.ceil(naturalHeight)}px`;
      } else {
        // Scale down to fit height
        const scale = availableHeight / naturalHeight;
        const fitWidth = Math.ceil(containerWidth * scale);
        svg.style.width = `${fitWidth}px`;
        svg.style.height = `${availableHeight}px`;
        svg.style.marginLeft = "auto";
        svg.style.marginRight = "auto";
      }
    });
}
The max height is configurable via CSS custom property --mermaid-max-height (default: 420px).

Error Handling

Invalid Mermaid syntax is handled gracefully:
function renderMermaidError(container, source) {
  if (container._panZoomInstance) {
    container._panZoomInstance.destroy();
    container._panZoomInstance = null;
  }
  
  const errorBlock = document.createElement("div");
  errorBlock.className = "mermaid-error";
  errorBlock.textContent = `Invalid mermaid syntax:\n${source}`;
  canvas.replaceChildren(errorBlock);
  addMermaidControls(container);
}
Error messages display the invalid source code, making it easy to debug syntax issues.

Editing Diagrams

Click the edit button to modify a diagram:
The block editor modal opens with:
  • Title: “Edit Mermaid diagram”
  • Textarea pre-filled with current source
  • Save and Cancel buttons
  • Keyboard shortcuts (Enter to save, Escape to cancel)
After saving:
  1. Source is stored in data-mermaid-source attribute
  2. Canvas is cleared and new mermaid node is created
  3. renderMermaidDiagrams() is called to re-render
  4. Controls are re-attached
  5. Change event is emitted for auto-save
if (state.blockEditType === "mermaid") {
  state.blockEditTarget.dataset.mermaidSource = source;
  const canvas = ensureMermaidCanvas(state.blockEditTarget);
  canvas.replaceChildren(createMermaidSourceNode(source));
  addMermaidControls(state.blockEditTarget);
  await renderMermaidDiagrams();
  closeBlockEditor();
  emitChange();
}

Best Practices

Keep diagrams focused: Large complex diagrams can be difficult to read. Consider splitting them into multiple smaller diagrams.
Test in fullscreen: Use fullscreen mode to verify your diagram looks good at different sizes before saving.
Syntax validation: Mermaid syntax errors prevent rendering. Use the Mermaid Live Editor to validate complex diagrams.

Build docs developers (and LLMs) love