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 (usemermaid.run()instead)
Initialization for Tab-Based Presentations
Critical Setting: startOnLoad: false
Why?
- Tabs with
display: noneprevent 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
Strategy 1: Render Once Per Tab (Recommended)
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:
loose (Recommended for Documentation)
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 → Usemermaid.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:
-
Check CDN loading:
console.log(typeof mermaid); // Should be 'object', not 'undefined' -
Check initialization:
console.log(mermaid.initialize); // Should be a function -
Check element visibility:
const el = document.querySelector('.mermaid'); console.log(window.getComputedStyle(el).display); // Should NOT be 'none' -
Check for syntax errors:
- Open browser console
- Look for Mermaid parsing errors
- Validate syntax at https://mermaid.live
-
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: falsefor 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 deprecatedinit())
❌ DON'T:
- Use
startOnLoad: truewith 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+