@shinyaz

Next.js で Mermaid 図を描画するにはクライアントサイドの動的インポートが必要

約1分

MDX ブログに Mermaid 対応を入れようとして、最初は Server Component 内で mermaid を呼んで SVG 文字列を生成しようとした。当然ながら mermaid は内部で getBBox 等の DOM API を使うため、サーバーサイドでは動かない。

結局クライアントコンポーネントで動的インポートする形に落ち着いた。ポイントは useEffect 内で import("mermaid") を呼ぶことでバンドルサイズへの影響を最小化する点と、next-themesresolvedTheme を依存配列に入れてテーマ切り替え時に再レンダリングさせる点だ。

"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 を使うと system 設定でも実際のテーマ("light" / "dark")が取れる。theme だと "system" が返ってくるので mermaid のテーマ選択に使えない。

共有する

田原 慎也

田原 慎也

ソリューションアーキテクト @ AWS

AWS ソリューションアーキテクトとして金融業界のお客様を中心に技術支援を行っています。クラウドアーキテクチャや AI/ML に関する学びをこのサイトで発信しています。このサイトの内容は個人の見解であり、所属企業の公式な意見や見解を代表するものではありません。