@shinyaz

Rendering Mermaid diagrams in Next.js requires client-side dynamic import

1 min read

I tried calling mermaid in a Server Component to generate SVG strings at build time. Predictably, mermaid uses DOM APIs like getBBox internally, so it fails on the server.

The solution is a client component with dynamic import inside useEffect. This keeps mermaid out of the initial bundle. The other key detail: use resolvedTheme from next-themes (not theme) in the dependency array so diagrams re-render on theme toggle.

"use client";
 
import { useEffect, useRef, useState } from "react";
import { useTheme } from "next-themes";
 
export function Mermaid({ chart }: { chart: string }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [svg, setSvg] = useState("");
  const { resolvedTheme } = useTheme();
 
  useEffect(() => {
    let cancelled = false;
    async function render() {
      const mermaid = (await import("mermaid")).default;
      mermaid.initialize({
        startOnLoad: false,
        theme: resolvedTheme === "dark" ? "dark" : "default",
      });
      const { svg } = await mermaid.render(
        `mermaid-${Math.random().toString(36).slice(2, 9)}`,
        chart,
      );
      if (!cancelled) setSvg(svg);
    }
    render();
    return () => { cancelled = true; };
  }, [chart, resolvedTheme]);
 
  return <div ref={containerRef} dangerouslySetInnerHTML={{ __html: svg }} />;
}

resolvedTheme returns the actual theme ("light" or "dark") even when the user has "system" selected. Using theme directly would give you "system", which isn't useful for picking a mermaid theme.

Share this post

Shinya Tahara

Shinya Tahara

Solutions Architect @ AWS

I'm a Solutions Architect at AWS, providing technical guidance primarily to financial industry customers. I share learnings about cloud architecture and AI/ML on this site.The views and opinions expressed on this site are my own and do not represent the official positions of my employer.