Files
2025-11-30 08:37:27 +08:00

10 KiB

Mermaid.js Guidelines for x-html-builder

This guide provides best practices for working with Mermaid.js diagrams in HTML presentations with tab-based navigation.

Version and CDN

Current Version: Mermaid v11.12.1 (latest stable as of 2025)

Recommended CDN:

<!-- Standard version -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>

<!-- ESM version (modern browsers) -->
<script type="module">
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
</script>

Why v11?

  • Latest features and bug fixes
  • Improved performance
  • Better error handling
  • mermaid.init() deprecated (use mermaid.run() instead)

Initialization for Tab-Based Presentations

Critical Setting: startOnLoad: false

Why?

  • Tabs with display: none prevent diagrams from rendering correctly
  • Diagrams need manual rendering when tab becomes visible
  • Allows precise control over rendering timing

Configuration:

mermaid.initialize({
  startOnLoad: false,      // Manual control for tab switching
  theme: 'default',        // or 'dark', 'forest', 'neutral'
  securityLevel: 'loose',  // For trusted content (allows HTML labels)
  logLevel: 1,             // 0=none, 1=error, 2=warn, 3=info
  flowchart: {
    useMaxWidth: true,     // Responsive diagrams
    htmlLabels: true       // Rich text support
  }
});

Rendering Strategies

Best for: Static content with tab navigation

document.addEventListener('DOMContentLoaded', async function() {
  // Initialize Mermaid
  mermaid.initialize({ startOnLoad: false });

  // Render initially active tab
  const activeTab = document.querySelector('.tab-content.active');
  if (activeTab && !activeTab.dataset.mermaidRendered) {
    await mermaid.run({
      nodes: activeTab.querySelectorAll('.mermaid'),
      suppressErrors: true
    });
    activeTab.dataset.mermaidRendered = 'true'; // Mark as rendered
  }
});

async function switchTab(tabName) {
  // ... tab switching logic ...

  // Render diagrams only if not already rendered
  const activeTab = document.getElementById(`${tabName}-tab`);
  if (activeTab && !activeTab.dataset.mermaidRendered) {
    await mermaid.run({
      nodes: activeTab.querySelectorAll('.mermaid'),
      suppressErrors: true
    });
    activeTab.dataset.mermaidRendered = 'true';
  }
}

Benefits:

  • Performance: Each tab rendered only once
  • No duplicate diagrams
  • Minimal re-rendering overhead

Strategy 2: Render All on Load

Best for: Small number of tabs (<5), simple diagrams

document.addEventListener('DOMContentLoaded', async function() {
  mermaid.initialize({ startOnLoad: false });

  // Render ALL diagrams at once
  await mermaid.run({
    querySelector: '.mermaid',
    suppressErrors: true
  });
});

Benefits:

  • Simple implementation
  • No tab-switch delays
  • Slower initial load for many diagrams

Strategy 3: Manual Rendering API

Best for: Dynamic content from API, programmatic diagram generation

const graphDefinition = `
  graph TD
    A[Client] --> B[Load Balancer]
    B --> C[Server1]
    B --> D[Server2]
`;

const { svg } = await mermaid.render('uniqueId', graphDefinition);
document.getElementById('diagram-container').innerHTML = svg;

Benefits:

  • Full control over rendering
  • Can modify SVG before insertion
  • Works with dynamic content

Common Errors and Solutions

Error Cause Solution
Diagrams not rendering in hidden tabs startOnLoad: true + display: none Use startOnLoad: false + mermaid.run() on tab switch
Duplicate diagrams Re-rendering already rendered content Cache rendered tabs with data-mermaid-rendered attribute
"mermaid.init is deprecated" Using old API Replace mermaid.init() with mermaid.run()
SVG sizing issues Rendering while element is hidden Add setTimeout(... , 50) before rendering to ensure tab is visible
Parsing errors break page Invalid Mermaid syntax Use suppressErrors: true in mermaid.run()
Fonts not loaded Rendering before fonts ready Wait for DOMContentLoaded + small delay (50-100ms)
Different themes on tabs Theme not applied to new renders Re-initialize theme before rendering: mermaid.initialize({ theme: 'dark' })

Performance Optimization

1. Lazy Rendering

Only render diagrams when tab becomes visible (Strategy 1).

2. Caching

Use data-mermaid-rendered attribute to avoid re-rendering:

if (!element.dataset.mermaidRendered) {
  await mermaid.run({ nodes: [element] });
  element.dataset.mermaidRendered = 'true';
}

3. Batch Rendering

Render multiple diagrams in one mermaid.run() call:

// Good: One call for all diagrams in tab
await mermaid.run({ nodes: tab.querySelectorAll('.mermaid') });

// Bad: Multiple calls
diagrams.forEach(d => mermaid.run({ nodes: [d] }));

4. Error Suppression (Production)

Prevent parsing errors from breaking the page:

await mermaid.run({ suppressErrors: true });

5. Async/Await

Always use async/await for predictable rendering:

async function renderDiagrams() {
  await mermaid.run({ ... }); // Wait for completion
  console.log('Rendering complete');
}

Security Levels

Choose based on content trust level:

mermaid.initialize({ securityLevel: 'loose' });
  • Allows HTML labels
  • Allows some scripts in labels
  • Use for: Internal docs, trusted content

strict (User-Generated Content)

mermaid.initialize({ securityLevel: 'strict' });
  • Blocks all scripts
  • Sanitizes HTML
  • Use for: User-submitted diagrams, public content

antiscript (Middle Ground)

mermaid.initialize({ securityLevel: 'antiscript' });
  • Removes scripts from output
  • Allows safe HTML

Code Examples

Complete Tab-Based Implementation

document.addEventListener('DOMContentLoaded', async function() {
  // Initialize Mermaid
  if (typeof mermaid !== 'undefined') {
    mermaid.initialize({
      startOnLoad: false,
      theme: 'default',
      securityLevel: 'loose',
      logLevel: 1,
      flowchart: {
        useMaxWidth: true,
        htmlLabels: true
      }
    });

    // Render initially active tab
    setTimeout(async () => {
      const activeTab = document.querySelector('.tab-content.active');
      if (activeTab && !activeTab.dataset.mermaidRendered) {
        try {
          await mermaid.run({
            nodes: activeTab.querySelectorAll('.mermaid'),
            suppressErrors: true
          });
          activeTab.dataset.mermaidRendered = 'true';
        } catch (err) {
          console.error('Mermaid rendering error:', err);
        }
      }
    }, 100);
  }

  // Tab switching
  async function switchTab(tabName) {
    // ... tab activation logic ...

    // Render diagrams if not already rendered
    if (typeof mermaid !== 'undefined') {
      const activeTab = document.getElementById(`${tabName}-tab`);
      if (activeTab && !activeTab.dataset.mermaidRendered) {
        setTimeout(async () => {
          try {
            await mermaid.run({
              nodes: activeTab.querySelectorAll('.mermaid'),
              suppressErrors: true
            });
            activeTab.dataset.mermaidRendered = 'true';
          } catch (err) {
            console.error('Mermaid rendering error on tab switch:', err);
          }
        }, 50);
      }
    }
  }
});

Error Handling with Fallback

async function renderMermaidSafe(element) {
  try {
    await mermaid.run({ nodes: [element] });
  } catch (err) {
    console.error('Mermaid error:', err);
    element.innerHTML = `
      <div style="color: red; padding: 10px; border: 1px solid red;">
        ⚠️ Diagram rendering failed. Check syntax.
      </div>
    `;
  }
}

Migration Notes: v10 → v11

Breaking Changes:

  • mermaid.init() is deprecated → Use mermaid.run()
  • ⚠️ Some theme options renamed (check docs)

Recommended Changes:

// Old (v10)
mermaid.init(undefined, '.mermaid');

// New (v11)
await mermaid.run({ querySelector: '.mermaid' });

CDN Update:

<!-- v10 -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>

<!-- v11 -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>

Compatibility:

  • All diagram types (flowchart, sequence, class, ER, etc.) work in v11
  • Themes are compatible
  • Configuration options mostly unchanged

Troubleshooting Checklist

When diagrams don't render:

  1. Check CDN loading:

    console.log(typeof mermaid); // Should be 'object', not 'undefined'
    
  2. Check initialization:

    console.log(mermaid.initialize); // Should be a function
    
  3. Check element visibility:

    const el = document.querySelector('.mermaid');
    console.log(window.getComputedStyle(el).display); // Should NOT be 'none'
    
  4. Check for syntax errors:

  5. Check timing:

    • Are you rendering before DOM is ready?
    • Is element visible when rendering?
    • Try adding setTimeout(... , 100) delay

Best Practices Summary

DO:

  • Use startOnLoad: false for tab-based layouts
  • Cache rendered tabs with data-mermaid-rendered
  • Use async/await for predictable rendering
  • Add error handling with suppressErrors: true
  • Wait for element visibility before rendering
  • Use mermaid.run() (not deprecated init())

DON'T:

  • Use startOnLoad: true with hidden tabs
  • Re-render already rendered diagrams
  • Render while element has display: none
  • Use mermaid.init() (deprecated)
  • Ignore parsing errors in production
  • Mix rendering strategies

Version: 1.0.0 Last Updated: 2025-11-06 Mermaid Version: v11.12.1 Compatible with: x-html-builder v2.3.1+