Next.js App RouterのMDXにMermaid図表を導入する

4分で読めます

はじめに

技術ブログやドキュメントサイトでは、フローチャートやシーケンス図などのダイアグラムを使ってシステム設計や処理の流れを視覚的に説明したい場面が多くあります。Mermaidは、テキストベースの記法でダイアグラムを描画できるツールです。

Next.js App RouterとMDXを使用したブログにMermaidダイアグラムを統合する実装方法を、コード例とともに紹介します。

Mermaid.jsとは

Mermaid.jsは、テキストベースの記法でフローチャート、シーケンス図、クラス図、ガントチャートなどを描画できるJavaScriptライブラリです。GitHubやNotionなど多くのプラットフォームでサポートされており、技術ドキュメントに広く利用されています。

たとえば、Mermaid.jsを使うと以下のようなテキスト記法から図表を生成できます。

Markdown
```mermaid
graph LR
  A[開始] --> B[処理]
  B --> C[終了]
```
Mermaid Diagram

この記事で実現すること

  • MDXファイル内でmermaidコードブロックを使用可能に
  • 既存のシンタックスハイライターとのコンフリクト回避
  • ライトモード・ダークモードへの対応方法
  • フローチャート、シーケンス図、クラス図、ガントチャートなど多様なダイアグラム

前提条件

  • Next.js 15以降(App Router使用)
  • next-mdx-remoteによるMDX処理
  • 既存のシンタックスハイライター使用時は、コンフリクト回避の実装が必要

Mermaidパッケージのインストール

必要なパッケージをインストールします。

Bash
npm install mermaid

TypeScript使用時は型定義も自動的に含まれます。

Mermaidコンポーネントの実装

Mermaidダイアグラムを表示するReactコンポーネントを作成します。クライアントサイドでレンダリングするため、"use client"ディレクティブが必要になります。

src/components/Mermaid.tsxTSX
"use client";

import { useEffect, useRef, useState } from "react";

interface MermaidProps {
  chart: string;
}

export function Mermaid({ chart }: MermaidProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let mounted = true;

    const renderDiagram = async () => {
      if (!containerRef.current) return;

      try {
        setError(null);

        // 動的にmermaidをインポート
        const mermaid = (await import("mermaid")).default;

        // 初期化
        mermaid.initialize({
          startOnLoad: false,
          theme: "default",
        });

        // ユニークなIDを生成
        const id = `mermaid-${Math.random().toString(36).slice(2, 11)}`;

        // SVGをレンダリング
        const { svg } = await mermaid.render(id, chart);

        if (mounted && containerRef.current) {
          containerRef.current.innerHTML = svg;
        }
      } catch (err) {
        if (mounted) {
          setError(
            err instanceof Error
              ? err.message
              : "ダイアグラムの描画に失敗しました"
          );
          console.error(err);
        }
      }
    };

    renderDiagram();

    return () => {
      mounted = false;
    };
  }, [chart]);

  if (error) {
    return (
      <div className="p-4 bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 rounded-lg">
        {error}
      </div>
    );
  }

  return (
    <div
      ref={containerRef}
      className="my-6 overflow-x-auto"
    />
  );
}

実装のポイント

このMermaidコンポーネントの実装では、以下の点に注意しています。

  1. 動的インポート: await import("mermaid")でコード分割し、必要なときだけMermaidをロードします
  2. クリーンアップ: unmount時のフラグ管理でメモリリークを防止します
  3. エラーハンドリング: 型安全なエラーチェック(instanceof Error)で適切にエラーメッセージを表示します
  4. ID生成: Math.random()による動的ID生成はuseEffect内で実行されるため、SSRとクライアントで差分が発生しません(useEffectはクライアントサイドでのみ実行されます)
  5. スタイリング: インラインスタイルではなくTailwindクラスを使用し、ダークモード対応を実現します

テーマ対応(オプション)

ダークモード対応が必要な場合は、next-themesなどのライブラリと組み合わせます。

TSX
import { useTheme } from "next-themes";

export function Mermaid({ chart }: MermaidProps) {
  const { resolvedTheme } = useTheme();
  
  useEffect(() => {
    let mounted = true;

    const renderDiagram = async () => {
      if (!containerRef.current) return;

      try {
        setError(null);
        const mermaid = (await import("mermaid")).default;

        mermaid.initialize({
          startOnLoad: false,
          theme: resolvedTheme === "dark" ? "dark" : "default",
        });

        const id = `mermaid-${Math.random().toString(36).slice(2, 11)}`;
        const { svg } = await mermaid.render(id, chart);

        if (mounted && containerRef.current) {
          containerRef.current.innerHTML = svg;
        }
      } catch (err) {
        if (mounted) {
          setError(
            err instanceof Error
              ? err.message
              : "ダイアグラムの描画に失敗しました"
          );
        }
      }
    };

    renderDiagram();

    return () => {
      mounted = false;
    };
  }, [chart, resolvedTheme]);
  
  // ...
}

Mermaidはdefaultdarkの他にも、forestneutralなど複数のテーマを提供しています。詳細は公式ドキュメントを参照してください。

MDXコンポーネントへの統合

next-mdx-remoteを使用している場合、mdx-components.tsxファイルでカスタムコンポーネントを定義します。

mdx-components.tsxTSX
import React from "react";
import type { MDXComponents } from "mdx/types";
import { Mermaid } from "./components/Mermaid";

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
    // preタグをカスタマイズ
    pre: ({ children, ...props }) => {
      // 型安全なチェック
      if (
        React.isValidElement(children) &&
        children.props &&
        typeof children.props.className === "string"
      ) {
        const className = children.props.className;
        const language = className.replace("language-", "");
        const codeString = children.props.children;

        // mermaidの場合は専用コンポーネントを使用
        if (language === "mermaid" && typeof codeString === "string") {
          return <Mermaid chart={codeString} />;
        }
      }

      // それ以外は通常のpreタグで表示
      return <pre {...props}>{children}</pre>;
    },
  };
}

コンフリクト回避の仕組み

MDXはpreタグとcodeタグの組み合わせでコードブロックを表現します。言語情報はcodeタグのclassNamelanguage-xxx形式で格納されるため、これを利用して分岐処理を行います。

Mermaid Diagram

この仕組みにより、既存のシンタックスハイライターに影響を与えずにMermaidを統合できます。

既存のコードブロックとの共存

Mermaidを導入しても、既存のシンタックスハイライターは正常に動作します。

TypeScript
// TypeScriptコード - 通常のシンタックスハイライトで処理
interface User {
  id: string;
  name: string;
  email: string;
}

function greetUser(user: User): string {
  return `Hello, ${user.name}!`;
}

カスタムpreコンポーネント内で言語判定を行っているため、mermaid以外のコードブロックは通常どおり処理されます。

MDXファイルでの使用方法

MDXファイル内で、mermaidコードブロックを使用してダイアグラムを記述します。

Markdown
```mermaid
graph TD
  A[開始] --> B[処理]
  B --> C[終了]
```

Mermaidの記法は多様で覚えることが多いですが、GitHub CopilotなどのAIアシスタントを活用すれば、記法を細かく理解していなくてもダイアグラムを作成できます。

「フローチャートを作成して」「この処理をシーケンス図にして」といった自然言語のプロンプトから、適切なMermaid記法を生成してくれます。

主な図の種類

Mermaidが対応している主なダイアグラムタイプは以下の通りです。

ダイアグラム名用途
Flowchart処理フロー・条件分岐の可視化
Sequence Diagramシステム間の通信・メッセージングの表現
Class Diagramクラス構造・継承関係の設計図
State Diagram状態遷移・ライフサイクルの表現
Entity Relationship Diagramデータベーススキーマ・テーブル関係
Ganttプロジェクトスケジュール・タスク管理
Pie Chart割合・比率の可視化
Git GraphGitブランチ戦略・履歴の表現
User Journeyユーザー体験・顧客行動の可視化
Mindmap思考整理・概念マップ

Mermaidの記法の詳細は公式ドキュメントを参照してください。

フローチャート

条件分岐やループを含む処理フローを視覚化できます。

Mermaid Diagram

シーケンス図

システム間の通信やAPIコールの流れを表現できます。

Mermaid Diagram

クラス図

オブジェクト指向設計におけるクラス構造を図示できます。

Mermaid Diagram

ガントチャート

プロジェクトのスケジュールやタスクの進行状況を可視化できます。

Mermaid Diagram

Mermaid統合で技術記事の表現力を向上

Next.js App RouterとMDXを使用したブログへのMermaidダイアグラム統合方法を紹介しました。

MDXとMermaidを組み合わせることで、技術記事やドキュメントの表現力が大きく向上します。フローチャート、シーケンス図、クラス図など、様々なダイアグラムを使って複雑な概念を視覚的に伝えることができます。

この記事は役に立ちましたか?

この記事をシェア

X
Facebook
はてな
URLをコピー
utsusieのプロフィール画像

utsusie

UI Designer / Web Director