Mermaid 状態遷移図エディタ
状態遷移図は、ひとつのものが取りうる状態と、その間を動かす出来事を示します。対象が「一生」を持つとき——稟議書、契約、審査中の書類——に向いています。見分け方は簡単で、ラベルが動作ではなく様子を表す語になっていれば状態遷移図です。承認待ち、差し戻し、却下。
差し戻しのある稟議フロー
状態は稟議書が「どうなっているか」で、矢印のラベルは「何が起きたか」です。差し戻しが二か所から戻ってきて、取下げはどこにも行かない——この非対称さこそ状態遷移図が一目で見せてくれるもので、フローチャートでは埋もれます。
stateDiagram-v2
state "承認待ち (課長)" as 課長待ち
state "承認待ち (部長)" as 部長待ち
[*] --> 起案中: 起案者が作成
起案中 --> 課長待ち: 申請
課長待ち --> 部長待ち: 課長が承認
課長待ち --> 差し戻し: 課長が差し戻す
部長待ち --> 承認済: 部長が承認
部長待ち --> 差し戻し: 部長が差し戻す
差し戻し --> 起案中: 起案者が修正
起案中 --> 取下げ: 起案者が取り下げる
承認済 --> [*]
取下げ --> [*]実例で理解する
1. 最小の状態機械
`[*]` は開始でも終了でもあります。どちらの意味になるかは、矢印のどちら側にあるかで決まります。
stateDiagram-v2
[*] --> 下書き
下書き --> 公開済: 公開する
公開済 --> [*]2. 空白を含む状態名の付け方
状態 ID に空白は使えませんが、`state "表示名" as ID` と書けば安全な ID のまま読みやすいラベルにできます。フローチャートで引用符付きラベルを使うのと同じ考え方です。日本語で書いていると全角空白が紛れ込みやすいので、この書き方を最初から癖にしておくと安全です。
stateDiagram-v2
state "レビュー待ち" as レビュー
state "修正依頼あり" as 修正
[*] --> レビュー
レビュー --> 修正: 指摘が付く
修正 --> レビュー: 修正を反映
レビュー --> [*]: 承認3. 合成状態
状態は内部にそれ自身の状態機械を持てます。ある段階に意味のある内訳があり、それを最上位に並べると煩雑になるときに使います。ここでは「処理中」の内側で起きていることをまとめています。
stateDiagram-v2
[*] --> 待機列
待機列 --> 処理中: ワーカーが取得
state 処理中 {
[*] --> 検証
検証 --> 変換: スキーマ一致
変換 --> 書込
書込 --> [*]
}
処理中 --> 成功: 例外なし
処理中 --> 失敗: 例外発生
失敗 --> 待機列: 再実行
成功 --> [*]4. 条件分岐 (choice)
`<<choice>>` は、出来事ではなく条件で分かれる分岐です。判断を図の上に残しつつ、それを「対象が留まっている状態」だと偽らずに済みます。
stateDiagram-v2
state 与信判定 <<choice>>
[*] --> 申込受付
申込受付 --> 与信判定: スコアリング実行
与信判定 --> 自動承認: スコアが 40 未満
与信判定 --> 人手審査: スコアが 40 以上
人手審査 --> 自動承認: 審査担当が可とする
人手審査 --> 否決: 審査担当が否とする
自動承認 --> [*]
否決 --> [*]5. 並行領域
合成状態の中に単独行のハイフン 2 つを置くと、同時に有効な領域に分かれます。フローチャートには本当に真似のできない、状態遷移図だけの表現です。
stateDiagram-v2
[*] --> 登録手続き
state 登録手続き {
[*] --> メール未確認
メール未確認 --> メール確認済: リンクを押した
--
[*] --> 本人確認未提出
本人確認未提出 --> 本人確認済: 書類を受理
}
登録手続き --> 利用可能: 両方完了
利用可能 --> [*]状態遷移図 構文早見表
`stateDiagram` ではなく `stateDiagram-v2` を使ってください。どちらも描画されますが、v2 が現在も開発が続いているレイアウトエンジンで、合成状態と並行状態の扱いが明確に優れています。
| 構文 | 意味 |
|---|---|
| stateDiagram-v2 | 図を開始する。`stateDiagram` も動くが旧レイアウト。 |
| [*] --> A | 初期状態——入口。 |
| A --> [*] | 終了状態。 |
| A --> B | きっかけを書かない遷移。 |
| A --> B: 出来事 | その遷移を起こす出来事をラベルにする。 |
| state "表示名" as id | 空白を含まない ID と、読みやすい表示名。 |
| state A { ... } | 内部に状態機械を持つ合成状態。 |
| -- | 合成状態の内側で並行領域に分ける。ハイフンはちょうど 2 つ。 |
| state x <<choice>> | 条件による分岐点。 |
| state f <<fork>> / <<join>> | 並行遷移への分岐と合流。 |
| note right of A: 文字 | 注記を付ける。`note left of` もある。 |
| direction LR | 上から下ではなく左から右に配置する。 |
状態遷移図を実際に壊す 6 つのエラー
Mermaid 11.12.2 で再現したものです。前半の 4 つは描画が止まります。後半の 2 つはもっと厄介で、何事もなく描画されたうえで、書いたつもりのものとは違う図を返してきます。
表示されるもの
描画はされるが、ひとつの状態が 2 つのラベルに割れている
原因
状態 ID の中に全角空白が入っています。日本語入力のまま書いていると最も混入しやすい文字で、しかも画面上では半角空白と見分けが付きません。Mermaid はこれを拒否せず、残りを説明として扱うこともしません。語ごとに別々の箱を作ります。出力された state ID を読んで実測しました。`[*] --> 検収 待ち` は「検収」と「待ち」という二つの状態を生み、矢印の先に付くのは前者だけで、後者はどこにもつながらないまま残ります。全角空白でも半角空白でも結果は同じでした。説明の記法自体は存在しますが、コロンが要ります——`待ち: 検収の完了を待っている` の形であり、この誤りはまさにそれと取り違えられたものです。
対処
`state "表示名" as ID` で宣言し、以後は ID だけで参照します。
stateDiagram-v2
[*] --> 検収 待ち
検収 待ち --> 完了stateDiagram-v2
state "検収待ち" as 検収
[*] --> 検収
検収 --> 完了表示されるもの
Parse error、末尾が: got 'INVALID'
原因
状態 ID にハイフンが入っています。`sashimodoshi-machi` のようなケバブケースはつい書きたくなりますが、ハイフンは遷移の矢印の始まりとして読まれます。
対処
ID は 1 語か下線でつなぎ、読ませたい文字は引用符付きの表示名に入れます。
stateDiagram-v2
[*] --> shinsa-chu
shinsa-chu --> 完了stateDiagram-v2
state "審査中" as shinsaChu
[*] --> shinsaChu
shinsaChu --> 完了表示されるもの
合成状態の内側で Parse error
原因
`{` で開いた合成状態が閉じられていません。閉じ波括弧は単独の行に置く必要があります。
対処
ブロックを閉じます。
stateDiagram-v2
[*] --> 外側
state 外側 {
[*] --> 内側stateDiagram-v2
[*] --> 外側
state 外側 {
[*] --> 内側
}表示されるもの
Lexical error on line N. Unrecognized text.
原因
並行領域の区切りのハイフンの数が違います。合成状態の内側で、単独行に、ちょうど 2 つです。3 つはまったく別のトークンになります。
対処
`--` を使います。
stateDiagram-v2
state 両方 {
[*] --> A
---
[*] --> B
}stateDiagram-v2
state 両方 {
[*] --> A
--
[*] --> B
}表示されるもの
Parse error on line 1、末尾が: got 'ID'
原因
存在しないバージョン指定です。`stateDiagram` と `stateDiagram-v2` の 2 つしかなく、`-v3` は 1 行目で落ちます。
対処
`stateDiagram-v2` を使います。
stateDiagram-v3
[*] --> 下書きstateDiagram-v2
[*] --> 下書き表示されるもの
描画はされるが、分岐点がふつうの状態として描かれる
原因
`<<choice>>` の宣言を、それを使う遷移より後に書いています。Mermaid は最初に名前が出てきた時点でその状態を作るので、あとから付けたステレオタイプは、すでに作られたものを変えません。
対処
疑似状態は、それを参照する遷移より前に宣言します。
stateDiagram-v2
[*] --> 与信判定
与信判定 --> 自動承認
与信判定 --> 人手審査
state 与信判定 <<choice>>stateDiagram-v2
state 与信判定 <<choice>>
[*] --> 与信判定
与信判定 --> 自動承認
与信判定 --> 人手審査描画についての覚え書き
いずれもこのサイトが使う Mermaid 11.12.2 で実測したものです。
全角空白は状態 ID を静かに壊す唯一の文字
日本語で図を書くときに実際に効いてくる差はここです。実測すると、フローチャートのノード ID に全角空白を入れた場合は Parse error で止まりますが、状態 ID に入れた場合は止まりません。状態が 2 つに割れたまま描画されます。エラーが出てくれるほうが親切だという珍しい例で、状態遷移図では自分で気付くしかありません。ラベルに空白を入れたくなったら、反射的に `state "…" as ID` を書くのが唯一の防ぎ方です。
高さは状態あたり約 114 ピクセル増える
実測では、3 状態の viewBox がおよそ 73×348、40 状態で 82×4566 でした。1 状態あたり約 114 ピクセルです。フローチャートと同じく幅はほとんど動かず、状態機械はひたすら下に伸びます。一生が長くて枝が浅いときは、図の中に `direction LR` を書くのが定石です。
stateDiagram も stateDiagram-v2 も描画される——これが罠
「`stateDiagram-v2` を使わないと何も描画されない」という説明をよく見かけますが、11.12.2 では正しくありません。どちらのキーワードもエラーなく描画されます。違いはレイアウトの質、とくに合成状態と並行状態の扱いで、古いほうを使っても警告は出ません。合成状態が窮屈に見えたり矢印の取り回しが妙なときは、図を書き直す前にどちらで開いているかを確かめてください。
ラベルは HTML なので PNG 書き出しは描き直しになる
フローチャート・クラス図・ER 図と同じく、状態のラベルは SVG の `<foreignObject>` の中に描かれます。ブラウザはこれを canvas にラスタライズすることを拒むため、このサイトの PNG 書き出しは、いったん素の SVG テキストのラベルで描き直してから出力します。PNG は正しく原寸で出ますが、文字組みは画面とごくわずかに異なります。
テーマは色を変えるだけで、レイアウトは変えない
同じソースを明るいテーマと暗いテーマで描画すると viewBox は完全に一致します。テーマを切り替えたせいで状態機械が組み直されることはありません。
別の図が向いている場合
ラベルが動詞——検証する、送信する、再試行する——になっているなら、それは一生ではなく手順を書いています。フローチャートのほうが正直です。いちばんはっきりした合図は、「この状態にあるものは何ですか」という問いに答えられないことです。
複数の部品がそれぞれ独自の一生を持ち、面白いのはその相互作用のほうだという場合は、部品ごとに状態遷移図を 1 枚ずつ書き、やり取りはシーケンス図に任せるほうが、巨大な状態機械 1 枚より確実に伝わります。
そしてすべての状態がすべての状態とつながっているなら、どう描いても毛玉にしかなりません。それはたいてい、状態のつもりで並べたものが実は自由に組み合わさるフラグだということです。その場合は、有効な組み合わせの表のほうが絵よりはるかに多くを語ります。
他の図の種類
執筆 Dominik Malsch · 最終更新: