はじめに
技術ブログやドキュメントサイトでは、フローチャートやシーケンス図などのダイアグラムを使ってシステム設計や処理の流れを視覚的に説明したい場面が多くあります。Mermaidは、テキストベースの記法でダイアグラムを描画できるツールです。
Next.js App RouterとMDXを使用したブログにMermaidダイアグラムを統合する実装方法を、コード例とともに紹介します。
Mermaid.jsとは
Mermaid.jsは、テキストベースの記法でフローチャート、シーケンス図、クラス図、ガントチャートなどを描画できるJavaScriptライブラリです。GitHubやNotionなど多くのプラットフォームでサポートされており、技術ドキュメントに広く利用されています。
たとえば、Mermaid.jsを使うと以下のようなテキスト記法から図表を生成できます。
```mermaid
graph LR
A[開始] --> B[処理]
B --> C[終了]
```
この記事で実現すること
- MDXファイル内で
mermaidコードブロックを使用可能に
- 既存のシンタックスハイライターとのコンフリクト回避
- ライトモード・ダークモードへの対応方法
- フローチャート、シーケンス図、クラス図、ガントチャートなど多様なダイアグラム
前提条件
- Next.js 15以降(App Router使用)
next-mdx-remoteによるMDX処理
- 既存のシンタックスハイライター使用時は、コンフリクト回避の実装が必要
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コンポーネントの実装では、以下の点に注意しています。
- 動的インポート:
await import("mermaid")でコード分割し、必要なときだけMermaidをロードします
- クリーンアップ: unmount時のフラグ管理でメモリリークを防止します
- エラーハンドリング: 型安全なエラーチェック(
instanceof Error)で適切にエラーメッセージを表示します
- ID生成:
Math.random()による動的ID生成はuseEffect内で実行されるため、SSRとクライアントで差分が発生しません(useEffectはクライアントサイドでのみ実行されます)
- スタイリング: インラインスタイルではなくTailwindクラスを使用し、ダークモード対応を実現します
テーマ対応(オプション)
ダークモード対応が必要な場合は、next-themesなどのライブラリと組み合わせます。
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はdefault、darkの他にも、forest、neutralなど複数のテーマを提供しています。詳細は公式ドキュメントを参照してください。
MDXコンポーネントへの統合
next-mdx-remoteを使用している場合、mdx-components.tsxファイルでカスタムコンポーネントを定義します。
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タグのclassNameにlanguage-xxx形式で格納されるため、これを利用して分岐処理を行います。
この仕組みにより、既存のシンタックスハイライターに影響を与えずにMermaidを統合できます。
既存のコードブロックとの共存
Mermaidを導入しても、既存のシンタックスハイライターは正常に動作します。
// TypeScriptコード - 通常のシンタックスハイライトで処理
interface User {
id: string;
name: string;
email: string;
}
function greetUser(user: User): string {
return `Hello, ${user.name}!`;
}
カスタムpreコンポーネント内で言語判定を行っているため、mermaid以外のコードブロックは通常どおり処理されます。
MDXファイルでの使用方法
MDXファイル内で、mermaidコードブロックを使用してダイアグラムを記述します。
```mermaid
graph TD
A[開始] --> B[処理]
B --> C[終了]
```
Mermaidの記法は多様で覚えることが多いですが、GitHub CopilotなどのAIアシスタントを活用すれば、記法を細かく理解していなくてもダイアグラムを作成できます。
「フローチャートを作成して」「この処理をシーケンス図にして」といった自然言語のプロンプトから、適切なMermaid記法を生成してくれます。
主な図の種類
Mermaidが対応している主なダイアグラムタイプは以下の通りです。
Mermaidの記法の詳細は公式ドキュメントを参照してください。
フローチャート
条件分岐やループを含む処理フローを視覚化できます。
シーケンス図
システム間の通信やAPIコールの流れを表現できます。
クラス図
オブジェクト指向設計におけるクラス構造を図示できます。
ガントチャート
プロジェクトのスケジュールやタスクの進行状況を可視化できます。
Mermaid統合で技術記事の表現力を向上
Next.js App RouterとMDXを使用したブログへのMermaidダイアグラム統合方法を紹介しました。
MDXとMermaidを組み合わせることで、技術記事やドキュメントの表現力が大きく向上します。フローチャート、シーケンス図、クラス図など、様々なダイアグラムを使って複雑な概念を視覚的に伝えることができます。