1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

mermaidを使ったフロー図の生成

1
Last updated at Posted at 2026-09-06

私は2017年の iMac で、VS Code を使って設計のメモを Markdown で書いています。構成やデータの流れも同じファイルに置きたくて、mermaid を使い始めました。

調べながら試して「この形が良さそうだ」というところに落ち着いたので、共有します。mermaid をこれから使う人向けの、基本の書き方と、私の iMac と VS Code での見方です。

1. mermaid とは

Markdown のコードブロックに書くと、そのまま図になる記法です。

こう書くと、

```mermaid
flowchart LR
    A[入口] --> B[処理] --> C[出口]
```

こう出ます。

画像ファイルと比べた利点は3つです。

  • 画像ファイルを作らなくていい。 図が Markdown の中に文字として入る
  • 直すのが速い。 箱を1つ足すのは1行足すだけ
  • 差分が見える。 git で「どこが変わったか」が行単位で分かる

Qiita・GitHub・VS Code は mermaid に対応しているので、貼るだけで図として表示されます。

2. 基本の書き方

箱と矢印

--> でつなぐだけです。名前だけ書くと、その名前が箱になります。

箱の中の文字を変えたいときは [ ] で囲みます。左側の A B は自分で付ける名前で、図には出ません。

箱の形

囲む記号で形が変わります。私は判断するところを { } の菱形にして、それ以外は四角のままにしています。

矢印の種類

--> が実線、==> が太い線、-.-> が点線です。私は「いつもの流れ」を実線、「例外のときだけ通る道」を点線にしています。

矢印に文字を付ける

| で挟みます。条件を書くところです。

箱をグループにする

subgraph で囲むと、枠が付いて名前が入ります。

end を書き忘れると図が丸ごと出なくなります。 エラーの場所も分かりにくいので、subgraph を書いたらその場で end も打っておくと楽です。

向き

flowchart の後ろの2文字が向きです。

書き方 向き
flowchart LR 左から右
flowchart TD 上から下
flowchart RL 右から左
flowchart BT 下から上

私は基本的に LR(左から右)を使っています。 縦向きは、箱が増えると下へ伸びて画面に収まらなくなるからです。⚠ Qiita に貼るときも、縦に長い図は下が切れることがあります。この記事の図が全部横向きなのはそのためです。

3. VS Code で見る

VS Code は、そのままでは mermaid を図として表示しません。拡張を1つ入れます。

拡張の検索で Markdown Preview Mermaid Support(作者 Matt Bierner)を入れてください。入れたら Markdown を開いて ⌘K を押してから V(Windows は Ctrl+K → V)でプレビューが右に出ます。

左で1行足すと、右の図がすぐ描き直されます。 保存を待たずに反映されるので、箱の並びを試しながら書けます。

4. 箱が増えて線が絡んできたら

ここからが、私が調べて「これが良さそうだ」と思った部分です。

箱が5個くらいまでは、そのままでも読めます。ところが10個を超えたあたりから線が交差し始めて、どこからどこへつながっているのか目で追えなくなりました。

mermaid には並べ方を選ぶ設定があります。何も書かないと dagre というやり方で並びますが、elk に替えられます。図の先頭に4行足すだけです。

```mermaid
---
config:
  layout: elk
---
flowchart LR
    A[入口] --> B[処理]
    B --> C[出口]
```

足すのは flowchart の上です。 Qiita の記事の一番上にも --- で囲んだ部分がありますが、それとは別物で、mermaid のコードブロックの中に書きます。

私の図と同じ形(箱9個・線13本・グループ3つ)で比べたのが、この2枚です。⚠ 図の中の名前だけ例に置き換えています。

dagreで描いた図。マスタから一覧表へ向かう線が3か所で交差している

dagre では、マスタから一覧表へ向かう線が3か所で交差しています。 同じ mermaid の先頭に4行足しただけの図がこちらです。

ELKで描いた同じ図。線は直角に折れて段がそろい、交差はない

交差は0になり、線は直角に折れて段がそろいました。 図の中身は1文字も変えていません。

ELK は Eclipse Layout Kernel という配置を計算するプログラムで、mermaid 11 から使えるようになりました。

elk の下にはもう1つ mergeEdges という設定がありますが、私は触っていません。 true にすると線が束ねられて見た目はすっきりするのですが、元の図に無いつながりが読めてしまいました(同じ経路に重ねて描くだけで、線が減るわけではないため)。書かなければ既定のままなので、そのままにしています。

5. ⚠ ただし VS Code のプレビューでは効きません

ここでつまずきました。

先頭に4行足しても、VS Code のプレビューでは図が何も変わりません。 エラーも警告も出ません。私は最初、自分の書き方を間違えたのだと思いました。

原因を切り分けるために、同じファイルに3つ並べて試しました。

試したこと 結果
① 何も指定しない図 表示される(mermaid そのものは動いている)
config: theme: forest を足す 箱が緑になる(先頭の config は届いている)
config: layout: elk を足す 何も変わらない

②が効いて③が効かないので、「config が読まれていない」のではなく「layout だけが効いていない」と分かりました。

拡張の中を見ると ELK の部品はちゃんと入っています。それでも動きません。ELK は動かすのに Web Worker という機能を使うのですが、VS Code のプレビューの中では作れません。失敗すると mermaid はエラーを出さずに元の並べ方へ戻るという動作でした。

失敗しても何も出ないので、動いていないことにすら気づけません。 Obsidian でも同じでした。

この Qiita のプレビューも同じです。 この記事に貼った4行入りのコードをそのまま Qiita に書いても、ELK では描かれません。

6. だから私はブラウザで見ています

ELK が効くのは、mermaid をブラウザが直接読み込んだときです。そこで、Markdown を渡すと図だけを抜き出して HTML にして開くスクリプトを作りました。

~/bin/mermaid_view.sh として保存します(~/bin が無ければ mkdir -p ~/bin で作ってください)。

#!/bin/sh
# Markdown の中の mermaid を ELK 配置の HTML にして開く
#   sh mermaid_view.sh <入力ファイル> [出力HTML]
python3 - "$@" <<'PY'
import sys, os, re, html, subprocess

TEMPLATE = """<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>__TITLE__</title>
<style>
  body{margin:0;padding:28px 24px 60px;background:#f8fafc;color:#0f172a;
       font-family:-apple-system,BlinkMacSystemFont,"Hiragino Sans","Noto Sans JP",sans-serif;}
  h1{font-size:17px;margin:0 0 22px;}
  .fig{background:#fff;border:1px solid #e2e8f0;border-radius:12px;
       padding:18px 20px 22px;margin:0 0 22px;overflow-x:auto;}
  .fig h2{font-size:13px;color:#64748b;font-weight:600;margin:0 0 12px;}
  .mermaid{display:flex;justify-content:center;min-height:60px;}
</style>
</head>
<body>
<h1>__TITLE__</h1>
__FIGURES__
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
import elkLayouts from 'https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0/dist/mermaid-layout-elk.esm.min.mjs';
mermaid.registerLayoutLoaders(elkLayouts);
mermaid.initialize({
  startOnLoad: true,
  layout: 'elk',
  elk: { mergeEdges: false },
  fontFamily: '-apple-system, "Hiragino Sans", "Noto Sans JP", sans-serif',
  flowchart: { htmlLabels: true, nodeSpacing: 40, rankSpacing: 80 },
});
</script>
</body>
</html>
"""

if len(sys.argv) < 2:
    print("使い方: sh mermaid_view.sh <入力ファイル> [出力HTML]", file=sys.stderr); sys.exit(1)

src = os.path.abspath(sys.argv[1])
if not os.path.exists(src):
    print("入力が見つかりません: " + src, file=sys.stderr); sys.exit(1)

lines = open(src, encoding="utf-8").read().split("\n")
blocks, heading, i = [], "", 0
while i < len(lines):
    line = lines[i]
    if re.match(r"^\s*`{3,}\s*mermaid\s*$", line):
        body, i = [], i + 1
        while i < len(lines) and not re.match(r"^\s*`{3,}\s*$", lines[i]):
            body.append(lines[i]); i += 1
        blocks.append((heading, "\n".join(body).strip()))
    elif re.match(r"^\s*`{3,}", line):          # mermaid 以外のコードブロックは飛ばす
        i += 1
        while i < len(lines) and not re.match(r"^\s*`{3,}\s*$", lines[i]):
            i += 1
    else:
        m = re.match(r"^(#{1,6})\s+(.*)$", line)
        if m:
            heading = m.group(2).strip()
    i += 1

if not blocks:
    print("mermaid のブロックが1つもありません: " + src, file=sys.stderr); sys.exit(2)

figs = []
for n, (head, body) in enumerate(blocks, 1):
    label = html.escape(head) if head else "図 " + str(n)
    figs.append('<div class="fig">\n  <h2>' + label + '</h2>\n'
                '  <pre class="mermaid">' + html.escape(body) + '</pre>\n</div>')

title = os.path.splitext(os.path.basename(src))[0]
out = sys.argv[2] if len(sys.argv) > 2 else os.path.join(
    os.path.dirname(src), title + "_図.html")

page = (TEMPLATE.replace("__TITLE__", html.escape(title))
                .replace("__FIGURES__", "\n".join(figs)))
open(out, "w", encoding="utf-8").write(page)

print("図 " + str(len(blocks)) + " 枚 → " + out)
subprocess.run(["open", out], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
PY

打ち方はこれだけです。

sh ~/bin/mermaid_view.sh "設計メモ.md"

同じフォルダに 設計メモ_図.html ができて、ブラウザが開きます。

動き方はこうです。

  • ファイルの中の mermaid を全部拾って、1枚の HTML に上から順に並べる(図が何枚あってもファイルは1つ)
  • 各図の上には、その図より前にある直近の見出しが載る
  • 文章・表・段落は載らない。 図だけを抜き出すスクリプトです
  • 同じ名前で毎回上書きするので、Markdown を直して打ち直せば図も新しくなります

npm install は要りません。 mermaid 本体と ELK はネットから読み込みます。⛔ 逆に言うと、つながっていないと図が出ません。

⭐ このスクリプトの中の HTML で layout: 'elk' を指定しているので、Markdown 側に4行を書いていなくても ELK で並びます。 4章の4行は、VS Code や GitHub にそのまま貼りたいときのためのものです。

まとめ

やりたいこと どうするか
図を Markdown に書く ```mermaid のコードブロックに flowchart LR
判断するところを分ける { } で菱形にする
例外の道を分ける -.-> の点線にする
箱をまとめる subgraph 名前 ... end
VS Code で見る 拡張 Markdown Preview Mermaid Support + ⌘K V
線の交差を減らす 先頭に config: layout: elk の4行
その4行を効かせる ⛔ VS Code では効かない。上のスクリプトでブラウザに出す

まず動かすなら、2章の書き方だけで十分です。 私も最初は --> でつなぐだけでした。線が絡んで読めなくなってから、4章から先を調べ始めました。同じところで止まっている人の助けになれば。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?