Batch diagram rendering & automation with Mermaid
When you're managing large documentation projects, internal training materials, or auto-generated architecture reports, hand-crafting each diagram isn't scalable. Batch rendering lets you define diagram templates once and generate dozens—or hundreds—of visual outputs programmatically. This guide shows how to automate Mermaid diagram generation in your build pipeline.
When to batch-render Mermaid diagrams
Batch rendering shines in these scenarios:
- Documentation pipelines: Generate architecture, API, or deployment diagrams for every microservice in your repo at once
- Automated reports: Build monthly compliance or dependency reports with fresh Mermaid visuals
- CI/CD integration: Create pull request diagrams showing deployment impacts or infrastructure changes
- Data-driven diagrams: Render flowcharts for each feature flag, rollout plan, or release strategy
- Accessibility-first workflows: Generate accessible SVG versions for screen readers across your entire docs site
The architecture of batch rendering
A typical pipeline has three steps:
- Template definition — Define reusable diagram skeletons (with placeholders)
- Data binding — Populate templates with dynamic content (service names, versions, metrics)
- Render & export — Convert bound diagrams to PNG/SVG and save to disk
Here's a conceptual flow:
flowchart LR
Templates["Diagram Templates<br/>(with placeholders)"] --> Bind["Bind Data<br/>(JSON, CSV, or<br/>database)"]
Services["Service Registry<br/>or Config"] --> Bind
Bind --> Render["Render to PNG/SVG<br/>(Node.js CLI or<br/>mermaid.js)"]
Render --> Output["Save to<br/>docs/dist/<br/>or CDN"]
Output --> Deploy["Deploy or<br/>publish"]
Option 1: Node.js CLI rendering
The @mermaid-js/mermaid-cli package is the fastest way to batch-render Mermaid files. Install it:
npm install --save-dev @mermaid-js/mermaid-cli
Create a directory of .mermaid or .md files, then render them all to PNG:
mmdc -i diagrams/*.mermaid -o dist/ -e png
For more control, use a Node.js script:
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const diagramsDir = 'diagrams/';
const outputDir = 'dist/';
fs.readdirSync(diagramsDir).forEach((file) => {
if (file.endsWith('.mermaid')) {
const input = path.join(diagramsDir, file);
const output = path.join(outputDir, file.replace('.mermaid', '.png'));
exec(`mmdc -i ${input} -o ${output} -e png`);
}
});
Option 2: Programmatic rendering with mermaid.js
For more control over rendering, use the mermaid npm package directly (Node.js + headless browser):
import mermaid from 'mermaid';
import * as fs from 'fs';
import puppeteer from 'puppeteer';
async function renderDiagram(diagramText, outputPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Set content with Mermaid diagram
await page.setContent(`
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
</head>
<body>
<div class="mermaid">${diagramText}</div>
<script>
mermaid.initialize({ startOnLoad: true, securityLevel: 'loose' });
mermaid.contentLoaded();
</script>
</body>
</html>
`);
// Wait for Mermaid to render
await page.waitForSelector('svg');
// Screenshot and save
await page.screenshot({ path: outputPath, type: 'png' });
await browser.close();
}
// Batch render all diagrams
const diagrams = [
{ text: 'flowchart LR\n A --> B', output: 'dist/flow1.png' },
{ text: 'sequenceDiagram\n A->>B: Hello', output: 'dist/seq1.png' },
];
for (const diagram of diagrams) {
await renderDiagram(diagram.text, diagram.output);
console.log(`✓ Rendered ${diagram.output}`);
}
Template-based rendering with data binding
For complex projects, define templates with placeholders and bind them to dynamic data:
Template: templates/service-diagram.mermaid
flowchart TD
Client["Client"]
Client -->|requests| Service["{{serviceName}}"]
Service -->|queries| DB["{{dbType}} ({{dbName}})"]
DB -->|replicates to| Backup["Backup: {{backupLocation}}"]
Service -->|publishes to| Queue["{{queueType}}"]
Data file: services.json
[
{ "serviceName": "Auth Service", "dbType": "PostgreSQL", "dbName": "users_db", "backupLocation": "us-west-2", "queueType": "Redis" },
{ "serviceName": "Orders API", "dbType": "MongoDB", "dbName": "orders_db", "backupLocation": "eu-central-1", "queueType": "Kafka" }
]
Binding script:
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync('templates/service-diagram.mermaid', 'utf-8');
const services = JSON.parse(fs.readFileSync('services.json', 'utf-8'));
services.forEach((service) => {
let diagram = template;
Object.keys(service).forEach((key) => {
diagram = diagram.replace(`{{${key}}}`, service[key]);
});
const outputPath = `dist/${service.serviceName.replace(/\s+/g, '-')}.mermaid`;
fs.writeFileSync(outputPath, diagram);
console.log(`✓ Generated ${outputPath}`);
});
Integration with CI/CD pipelines
Add batch rendering to your GitHub Actions or GitLab CI workflow:
# .github/workflows/generate-diagrams.yml
name: Generate Diagrams
on:
push:
paths:
- 'services.json'
- 'templates/**'
jobs:
render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Render diagrams
run: node scripts/batch-render.js
- name: Commit and push
run: |
git add dist/
git commit -m "docs: auto-generated diagrams"
git push
Batch rendering best practices
- Version your templates: Keep templates in git alongside their data files for reproducibility
- Organize output: Use subdirectories in
dist/to organize diagrams by type (architecture, deployment, flows) - Cache aggressively: Skip re-renders for unchanged input; store diagram hashes or compare timestamps
- Monitor rendering time: For large batches, consider parallelizing renders or sampling for preview
- Test examples: Include a few hand-crafted reference diagrams to catch rendering issues early
- Use SVG for docs: SVG diagrams scale and remain crisp; PNG works best for static exports and reports
Common pitfalls
Diagram syntax errors in data: Validate placeholder values—bad characters in node labels will break Mermaid syntax. Escape quotes and special characters:
const escapeLabel = (label) => label.replace(/"/g, '\\"').replace(/\n/g, ' ');
Browser timeout in puppeteer: Mermaid can take time to render complex diagrams. Add explicit waits:
await page.waitForSelector('svg', { timeout: 30000 }); // 30s timeout
Missing fonts in headless rendering: Headless browsers may not include all system fonts. Embed web fonts in your HTML template for consistent output.
FAQ
Can I batch-render from within CI/CD without installing a CLI?
Yes—use the Node.js mermaid package directly with puppeteer in your CI runner. This avoids external CLI dependencies.
What's the performance difference: PNG vs. SVG for large batches? SVG renders 2–3× faster (no pixel rasterization), but PNG is more compatible with legacy tools and email clients. Render to both if you need flexibility.
How do I handle diagrams that exceed rendering size limits?
Split large diagrams into sub-diagrams with internal links, or use Mermaid's %%{init: {maxLength: 50000}} directive to increase the limit (careful with browser memory).
Ready to scale your diagram generation? Use MermaidCreator to prototype a diagram template visually, export the Mermaid syntax, then wire it into your batch pipeline.
Related posts
Mermaid in pull requests: automated diagram comments
Add architecture diagrams to PRs automatically. Document changes, share context, and let reviewers visualize impact—all without manual steps.
Testing Mermaid diagrams in CI/CD: catch breaking changes before merge
Automate diagram validation, render testing, and syntax checks in your pipeline. Keep diagrams accurate alongside your code.