How to Integrate Mermaid into a React Component Using the Promise-Based API
Use mermaid.render() inside a useEffect hook to asynchronously generate SVG markup from diagram definitions, then inject the result using dangerouslySetInnerHTML while handling loading states and cleanup.
The mermaid-js/mermaid library provides a client-side JavaScript API that converts text-based diagram definitions into scalable vector graphics. When you integrate Mermaid into a React component, you work with the asynchronous mermaid.render() method, which returns a Promise that resolves with the generated SVG string. This approach requires careful management of component lifecycles, cleanup logic, and state updates to prevent memory leaks and ensure smooth re-renders.
Understanding the Mermaid Render API
According to the source code in src/mermaidAPI.js, the mermaid.render() function serves as the primary entry point for generating diagrams programmatically. This method accepts two parameters: a unique string id for the diagram instance and the diagramDefinition string containing the Mermaid syntax.
The function returns a Promise that resolves to an object with the following structure:
svg: The generated SVG markup as a stringbindFunctions: Optional callback for attaching event listeners to diagram elementsbindListeners: Optional callback for additional listener bindingserrors: Array of any parsing or rendering errors encountered
The Importance of Unique IDs
When calling mermaid.render(id, definition), the id parameter must be unique across your application to prevent DOM collisions. In React 18+, use the useId() hook to generate stable, unique identifiers for each diagram instance. For older React versions, implement a counter or UUID generator to ensure uniqueness.
Step-by-Step Integration Guide
To properly integrate Mermaid into a React component, you must initialize the library, handle the asynchronous render cycle, and safely inject the resulting SVG. The implementation in src/index.js exports the public API including mermaid.initialize and mermaid.render.
Initializing the Library
Before rendering diagrams, call mermaid.initialize(options) once to configure global settings. This function, defined in src/initialize.js, sets the theme, security level, and other defaults. Pass startOnLoad: false when rendering manually within React components to prevent automatic DOM manipulation.
import mermaid from 'mermaid'
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
})
Handling the Render Promise
Because mermaid.render() executes asynchronously, implement cleanup logic to handle component unmounting. Use a cancelled flag inside useEffect to ignore Promise resolutions after the component has unmounted.
The render process creates a temporary DOM container internally (as implemented in src/diagramAPI.js), parses the diagram definition, and generates the SVG markup. This operation relies on window and DOM APIs, making it incompatible with server-side rendering unless properly guarded.
Complete React Component Example
Below is a production-ready React component that safely integrates Mermaid using the Promise-based API:
import { useEffect, useState, useId } from 'react'
import mermaid from 'mermaid'
// Initialize once at module level
mermaid.initialize({
startOnLoad: false,
theme: 'default'
})
export default function Mermaid({ definition, className = '' }) {
const [svg, setSvg] = useState('')
const [error, setError] = useState(null)
const diagramId = useId()
useEffect(() => {
let cancelled = false
mermaid
.render(diagramId, definition)
.then(({ svg }) => {
if (!cancelled) setSvg(svg)
})
.catch((err) => {
if (!cancelled) setError(err)
console.error('Mermaid render error:', err)
})
return () => {
cancelled = true
}
}, [definition, diagramId])
if (error) {
return <pre className={className}>Error rendering diagram: {error.message}</pre>
}
return (
<div
className={className}
dangerouslySetInnerHTML={{ __html: svg }}
/>
)
}
Managing Multiple Diagrams and Dynamic Themes
For applications rendering multiple diagrams or supporting theme switching, batch the render operations using Promise.all. The src/defaultConfig.js file contains the default theme options that can be overridden via mermaid.initialize().
import { useEffect, useState, useId } from 'react'
import mermaid from 'mermaid'
export function MermaidMulti({ diagrams, theme = 'neutral' }) {
const [svgs, setSvgs] = useState([])
const componentId = useId()
useEffect(() => {
// Re-initialize when theme changes
mermaid.initialize({ startOnLoad: false, theme })
const promises = diagrams.map((def, idx) => {
const id = `${componentId}-${idx}`
return mermaid.render(id, def).then(({ svg }) => ({ id, svg }))
})
Promise.all(promises)
.then((results) => setSvgs(results))
.catch((err) => console.error('Batch render error:', err))
}, [diagrams, theme, componentId])
return (
<>
{svgs.map(({ id, svg }) => (
<div key={id} dangerouslySetInnerHTML={{ __html: svg }} />
))}
</>
)
}
Error Handling and SSR Considerations
When integrating Mermaid into a React component, consider these edge cases derived from the source implementation:
Unique ID Generation: Always use useId() or a reliable counter to prevent ID collisions when multiple instances render simultaneously.
Server-Side Rendering Guard: Mermaid requires the window object and DOM APIs available only in browsers. Wrap initialization and render calls with if (typeof window !== 'undefined') checks, or use dynamic imports with ssr: false in Next.js.
Error Boundaries: The mermaid.render() Promise may reject on syntax errors or when parsing invalid diagram definitions. Always implement .catch() handlers to prevent unhandled Promise rejections and display fallback UI.
Cleanup on Unmount: As shown in the examples, use a cancelled boolean flag to prevent calling setState after component unmounting, which would trigger React warnings.
Key Source Files in the Mermaid Repository
Understanding these implementation files helps debug integration issues:
| File | Purpose |
|---|---|
src/mermaidAPI.js |
Contains the core render() method that returns a Promise |
src/initialize.js |
Implements mermaid.initialize() for global configuration |
src/diagramAPI.js |
Handles diagram parsing and temporary DOM container creation |
src/index.js |
Entry point exporting the public API |
src/defaultConfig.js |
Default theme and rendering options |
Summary
- Initialize once: Call
mermaid.initialize()at the module level or inside auseEffectwith an empty dependency array - Handle async rendering: Use
mermaid.render()insideuseEffectto generate SVG strings asynchronously - Prevent memory leaks: Implement cleanup flags to ignore Promise results after component unmount
- Inject safely: Use
dangerouslySetInnerHTMLto render the resolved SVG markup - Guard for SSR: Ensure Mermaid code only executes in browser environments with
typeof window !== 'undefined'checks
Frequently Asked Questions
How do I prevent memory leaks when using mermaid.render() in React?
Implement a cancelled flag inside your useEffect hook. Set the flag to true in the cleanup function returned by useEffect, and check this flag before calling setState in your Promise .then() handler. This prevents state updates on unmounted components.
Can I use Mermaid with Next.js or other SSR frameworks?
Mermaid requires browser-only APIs like window and document. For Next.js, use dynamic imports with ssr: false or check typeof window !== 'undefined' before importing or calling Mermaid functions. Alternatively, use the next/dynamic import to load the component only on the client side.
What does the mermaid.render() Promise return?
The Promise resolves to an object containing { svg, bindFunctions, bindListeners, errors }. The svg property contains the generated markup as a string. The errors array contains any parsing warnings, while bindFunctions and bindListeners provide callbacks for attaching interactivity to diagram elements.
How do I update a diagram when the definition changes?
Include the diagram definition string in the useEffect dependency array. When the definition prop changes, React re-runs the effect, triggering a new mermaid.render() call with the updated text. Ensure you also include the unique id in dependencies to maintain consistency between render cycles.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →