Building a Mermaid Diagram Editor: From DSL Text to SVG Rendering#

I was writing documentation the other day and needed a flowchart. Opened draw.io, dragged some shapes around, felt tedious. Then I remembered Mermaid - write simple text syntax, get diagrams. Decided to build an online Mermaid editor with real-time preview and quick export.

What is Mermaid?#

Mermaid is a JavaScript-based diagramming tool. You write text, it generates flowcharts, sequence diagrams, Gantt charts, and more. For example:

flowchart TD
  A[Start] --> B{Pass?}
  B -->|Yes| C[Process]
  B -->|No| D[Retry]
  C --> E[End]
  D --> B

This “Diagrams as Code” approach is better for version control than drag-and-drop tools. Changes show up clearly in git diffs.

Core Implementation: Dynamic Loading + Debounced Rendering#

Mermaid is heavy - about 1MB minified. We use dynamic imports to avoid slowing down the initial page load:

const mermaidRef = useRef<any>(null)

useEffect(() => {
  const loadMermaid = async () => {
    const mermaid = (await import('mermaid')).default
    mermaid.initialize({
      startOnLoad: false,
      theme: 'dark'
    })
    mermaidRef.current = mermaid
    setRenderId(1) // trigger first render
  }
  loadMermaid()
}, [])

The library only loads when the user visits the Mermaid editor page.

Real-time preview needs debouncing. Rendering on every keystroke would be slow:

const debounceRef = useRef<NodeJS.Timeout | null>(null)

useEffect(() => {
  if (renderId === 0) return
  if (debounceRef.current) clearTimeout(debounceRef.current)
  debounceRef.current = setTimeout(() => {
    renderMermaid(code)
  }, 500)
  return () => {
    if (debounceRef.current) clearTimeout(debounceRef.current)
  }
}, [code, renderId])

We wait 500ms after the user stops typing before rendering.

DSL Parsing and Error Handling#

Mermaid has a parser that converts text DSL to an AST, then renders SVG. Syntax errors throw exceptions:

const renderMermaid = async (value: string) => {
  if (!mermaidRef.current || !value.trim()) {
    setSvg('')
    setError('')
    return
  }
  try {
    const id = `mermaid-${Date.now()}`
    const { svg } = await mermaidRef.current.render(id, value)
    setSvg(svg)
    setError('')
  } catch (err: any) {
    setError(err?.message || String(err))
    setSvg('')
  }
}

We use Date.now() for unique IDs to avoid conflicts when rendering multiple diagrams. Error messages show directly to help users fix syntax issues.

SVG Export and Memory Cleanup#

After rendering, Mermaid returns an SVG string. Export creates a Blob and triggers download:

const handleDownloadSvg = () => {
  if (!svg) return
  const blob = new Blob([svg], { type: 'image/svg+xml' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = 'diagram.svg'
  a.click()
  URL.revokeObjectURL(url) // free memory
}

The key is URL.revokeObjectURL(url) - without it, each export leaks a Blob URL in memory.

Template Switching Design#

We provide 7 templates: flowchart, sequence diagram, class diagram, state diagram, Gantt chart, pie chart, ER diagram. Templates live in an object, switching just replaces the editor content:

const templates: Record<string, string> = {
  flowchart: `flowchart TD
    A[Start] --> B{Decision}
    B -->|Yes| C[Action 1]
    B -->|No| D[Action 2]`,
  sequence: `sequenceDiagram
    participant A as Alice
    participant B as Bob
    A->>B: Hello Bob`,
  // ... other templates
}

const handleTemplateChange = (key: string) => {
  setSelectedTemplate(key)
  setCode(templates[key])
}

Object dictionary is cleaner than switch-case. Adding a template is just one key-value pair.

Performance Edge Cases#

We hit a few performance issues:

  1. Large diagrams: 100+ nodes take 2-3 seconds to render. Show a loading spinner so users don’t think it’s frozen.

  2. Rapid template switching: Each switch triggers debounced re-render. Clear the timeout immediately on switch to avoid wasted renders.

  3. Huge SVG size: Complex diagrams can produce 500KB+ SVGs. Tools like SVGO can optimize by removing comments and redundant attributes.

Use Cases#

Mermaid shines for:

  • Technical docs: Embed flowcharts in README, Markdown supports it natively
  • Architecture design: Sequence diagrams for API flows, class diagrams for object relationships
  • Project planning: Gantt charts for schedules, state diagrams for business workflows

Compared to drag-and-drop tools, Mermaid is version-control friendly, reusable, and maintainable. Changing a diagram is just editing text - diffs are clear.

If you work with diagrams and visualizations, these might help too:


Keywords: Mermaid diagram editor, DSL rendering, flowchart generator, sequence diagram, SVG export, diagram visualization tool