beautiful-mermaid でゼロ依存の Mermaid 図を描く
ゼロ依存・DOM 非依存の beautiful-mermaid を Astro + MDX に組み込み、build 時の静的レンダリングと runtime のインタラクティブエディタを共存させた実装メモ。renderMermaidSVG が同期で SVG 文字列を返す設計、公式 mermaid との機能差、50 ノード超で build が伸びる閾値まで。
検証日: 2026-05-26
使用バージョン:
beautiful-mermaid@1.1.3(2026-05 時点でmain)環境: Node 22.12 / Astro 6.3 / MDX 5
技術記事に 図 を入れたい場面はよくあるが、Mermaid 公式ライブラリ(mermaid)は重く、DOM 依存があるためサーバーサイドでの静的 SVG 生成が大変。
beautiful-mermaid は ゼロ依存・DOM 非依存 で SVG を同期 render できるので、Astro の build 時にそのまま図に変換できる。
利用形態は 2 つ:
- 静的に埋め込む(
<Mermaid />Astro コンポーネント、build 時 SVG) - 触れるエディタ(
<MermaidEditor />React コンポーネント、runtime 編集 + 即時再描画)
セットアップ(npm 1 行 / Node / Browser / Deno / Bun)
npm i beautiful-mermaid
| 項目 | 値 |
|---|---|
| 公開バージョン | 1.1.3(2026-05) |
| Node 必要バージョン | 18+ |
| TypeScript 型 | 本体同梱 |
| 依存ライブラリ | 0(他の npm パッケージを引きずらない) |
| DOM 依存 | なし(jsdom / canvas 不要、純粋関数として SVG 文字列を返す) |
| ライセンス | MIT |
公式 mermaid パッケージは DOM 依存があり、Node 上で SSR したい時は jsdom 経由で重い初期化が必要。beautiful-mermaid の renderMermaidSVG(code) は 同期関数で SVG 文字列を返す ので、Astro の set:html にそのまま流せる。
import { renderMermaidSVG } from "beautiful-mermaid";
const svg: string = renderMermaidSVG("graph TD\nA-->B");
// → "<svg xmlns=...>...</svg>"
公式 mermaid との機能差(2026-05 時点)
| 機能 | 公式 mermaid | beautiful-mermaid v1 |
|---|---|---|
| flowchart / graph | ✅ | ✅ |
| sequenceDiagram | ✅ | ✅ |
| classDiagram | ✅ | ✅ |
| erDiagram | ✅ | ✅ |
| stateDiagram | ✅ | △(限定的) |
| gantt | ✅ | ❌ |
| mindmap | ✅ | ❌ |
| journey | ✅ | ❌ |
%%{init: ...}%% テーマ | ✅ | ❌(v1 では未対応) |
| 同期 SVG 生成 | ❌(非同期 + DOM 必要) | ✅(同期、DOM 不要) |
| DOM 依存 | あり | なし |
| 依存パッケージ数 | 数十(d3 / dagre 等) | 0 |
「記事に flowchart / sequence / ER を埋めたい」用途は beautiful-mermaid が圧倒的にラク(SSR で SVG 焼ける、初回描画ゼロ wait)。「gantt / mindmap / theme カスタマイズが必要」なら公式 mermaid を browser ロードする。
静的埋め込み 1 — Astro コンポーネント(build 時 SVG)
<Mermaid code={...} /> を MDX から呼ぶだけで、build 時に SVG が埋め込まれる。
シーケンス図も同じインタフェース。
触れる Mermaid エディタ
プリセットからテンプレートを選んで編集してみてください。左を変えると右が即座に再描画されます。
beautiful-mermaid(ゼロ依存、build/ runtime 両対応)。実装の中身
Astro コンポーネント(static)
サーバー側で renderMermaidSVG(code) を呼んで、結果の SVG 文字列を set:html で埋め込むだけ:
---
import { renderMermaidSVG } from "beautiful-mermaid";
interface Props {
code: string;
caption?: string;
maxWidth?: string;
}
const { code, caption, maxWidth = "100%" } = Astro.props;
let svg = "";
let error: string | null = null;
try {
svg = renderMermaidSVG(code);
} catch (e) {
error = (e as Error).message ?? "Mermaid render error";
}
---
<figure class="not-prose my-6">
<div class="overflow-x-auto p-4 border border-line rounded-lg" style={`max-width: ${maxWidth}`}>
{error ? <pre>{error}</pre> : <div set:html={svg} />}
</div>
{caption && <figcaption>{caption}</figcaption>}
</figure>
React コンポーネント(interactive)
ブラウザでも renderMermaidSVG が動くので、useMemo で code → SVG を再計算する interactive editor が組める:
import { useEffect, useMemo, useRef, useState } from "react";
import { renderMermaidSVG } from "beautiful-mermaid";
export function MermaidEditor({ initialCode }: { initialCode?: string }) {
const [code, setCode] = useState(initialCode ?? PRESETS[0].code);
const svg = useMemo(() => {
try {
return renderMermaidSVG(code);
} catch (e) {
return "";
}
}, [code]);
// ...textarea + preview の 2 列 layout
}
useMemo で code が変わるたびに再 render。beautiful-mermaid は同期 + ゼロ依存なので、setState の度に呼んでも安定して動く。
表示の調整(向き / サイズ / 余白)
ある程度のスタイル調整は Mermaid コード自体のディレクティブ で済む。
方向(graph LR vs graph TD)
TD = Top-Down、LR = Left-Right。横に長い図は LR が読みやすい。
Subgraph(クラスタリング)
関連ノードをグルーピングして見せたい時は subgraph。
サイズ調整(maxWidth prop)
<Mermaid maxWidth="480px" /> で図の最大幅を絞れる(コンテナに pad されて中央揃え)。
小さく見せたい補足図に有効。
エラーハンドリング — 不正な構文を入れた時
renderMermaidSVG は構文エラーで例外を投げる(空文字列ではない)。Astro 側で try/catch し、error を fallback として <pre> で表示するのが安全:
---
import { renderMermaidSVG } from "beautiful-mermaid";
const { code } = Astro.props;
let svg = "";
let error: string | null = null;
try {
svg = renderMermaidSVG(code);
} catch (e) {
error = (e as Error).message;
// build を停止させたくない場合は console.warn のみで継続
}
---
{error ? <pre>{error}</pre> : <div set:html={svg} />}
MermaidEditor(React)側でも useMemo の中で try/catch しないと、入力ミスのたびに React tree 全体が unmount される。
5 つの落とし穴と直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
| MDX 内の複数行 Mermaid コードで改行が壊れる | 文字列リテラルが "..." の途中で切れる | code={\graph TD\nA—>B`}` のように template literal で渡す |
<ISBN> のような < が消える | MDX が tag と解釈 | label 内で < を使わない、もしくは < を埋めるか template literal で escape |
| theme を変えたい | v1 系は %%{init: ...}%% 未対応 | CSS 変数で color-mermaid-line を上書き、もしくは出力 SVG を後処理 |
| gantt / mindmap が描けない | v1 系で 未サポート | 公式 mermaid を browser ロード、または別ライブラリ(@nivo、d3)で代替 |
| 50 ノード超の図で build が伸びる | 1 図あたり ~100ms 程度かかる | 1 図 5-15 ノード × 図 10 個 を上限目安。重い図は画像化(svg を SVG ファイルに書き出して <img> 参照) |
評価
| 観点 | 評価 | コメント |
|---|---|---|
| 学習コスト | ◎ | API は render(code, { format }) の 1 つだけ |
| 依存 | ◎ | ゼロ依存(npm install で他に何も入らない) |
| 環境互換性 | ◎ | Node / browser / Deno / Bun いずれも動く |
| ダイアグラム種別 | ○ | flowchart / sequence / class / ER は OK、gantt / mindmap は限定的 |
| カスタマイズ | △ | テーマやフォントの細かい調整は v1 系では限定的 |
向く / 向かないケース
- 向く: 技術記事 / ドキュメントへの図埋め込み(flowchart / sequence / class / ER 中心)、サーバーサイド SVG 生成、AI agent の図出力
- 向かない: gantt や複雑な mindmap が必要な場面 → 公式
mermaidを browser ロード - 向かない: テーマ / 色 / フォントの厳密なブランド統制が必要な場面 → SVG を後処理するか、別のダイアグラムライブラリを検討
関連 Topic / 関連書籍
この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:
Effective TypeScript(第2版) : 型システムの力を最大限に引き出す83項目 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
TypeScriptとReact/Next.jsでつくる実践Webアプリケーション開発 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
かんたん TypeScript | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
ゼロからわかる TypeScript入門 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。