/ concept-collection / mdshare
concept-collection / mdshare
mdshare / src / Mermaid.tsx
37 lines · 1.4 KBBlameHistoryRaw
1import { useEffect, useRef, useState } from 'react'
3let idCounter = 0
5// Renders a ```mermaid fence. The mermaid library (~1 MB) is imported lazily
6// the first time a diagram appears, so documents without diagrams never load
7// it. securityLevel 'strict' because documents arrive from shared links.
8export default function Mermaid({ code }: { code: string }) {
9 const [svg, setSvg] = useState<string | null>(null)
10 const [error, setError] = useState<string | null>(null)
11 const idRef = useRef(`mermaid-${idCounter++}`)
13 useEffect(() => {
14 let cancelled = false
15 setSvg(null)
16 setError(null)
17 ;(async () => {
18 try {
19 const mermaid = (await import('mermaid')).default
20 mermaid.initialize({ startOnLoad: false, securityLevel: 'strict' })
21 const { svg } = await mermaid.render(idRef.current, code)
22 if (!cancelled) setSvg(svg)
23 } catch (e) {
24 if (!cancelled) setError(e instanceof Error ? e.message : String(e))
25 // mermaid.render leaves an error element in the body on parse failure
26 document.getElementById(`d${idRef.current}`)?.remove()
27 }
28 })()
29 return () => {
30 cancelled = true
31 }
32 }, [code])
34 if (error) return <pre className="mermaid-error">mermaid: {error}</pre>
35 if (svg === null) return <div className="mermaid-loading">rendering diagram…</div>
36 return <div className="mermaid-diagram" dangerouslySetInnerHTML={{ __html: svg }} />