React Flowは、Reactでノード(箱)とエッジ(線)をつないだ図を、ドラッグや接続の操作つきで作るためのライブラリです。ワークフロービルダー、ノードエディタ、状態遷移図などを数十行で動かせます。現在の正式パッケージは @xyflow/react で、2026年10月時点の最新安定版は2026年9月24日公開の12.12.0です。ライセンスはMITで商用利用も無料ですが、画面右下の帰属表示を消すときだけ公式は有料のProへの加入を求めています。この記事では、12.12.0とReact 19.3.1の組み合わせで実際に描画を確認したコードを使い、導入・型付け・保存・自動レイアウト・移行までを順に解説します。
まとめ:React Flowの要点
- React FlowはReact専用のノードベースUIライブラリ。Svelte版は同じ開発元のSvelte Flow、Vueは別プロジェクトのVue Flowを使う。Pythonでは動かない。
- 正式パッケージは
@xyflow/react(最新12.12.0)。reactflow(v11)とreact-flow-renderer(v10・非推奨)は旧名。 - ライセンスはMIT。商用利用・改変・再配布は無料。帰属表示は
proOptionsで消せるが、公式の方針は「消すならPro(月額169ドルから)に加入」。 - 状態は「ノード配列」と「エッジ配列」を別々に持ち、変更は
applyNodeChanges/applyEdgeChangesで反映する。 - 保存は
toObject()でノード・エッジ・表示位置をJSON化し、setNodes/setEdges/setViewportで戻す。 - v13は2026年10月11日に
13.0.0-next.0がプレリリースとして出た段階。React 18以上・ESMのみに変わるため、新規案件は12.12で始めてよい。
React Flowとは:ノードベースUIを作るReactライブラリ
公式サイトは React Flow を「A customizable React component for building node-based editors and interactive diagrams」と説明しています(reactflow.dev)。ノードのドラッグ移動、ハンドルからの線の接続、ズームとパン、範囲選択、削除、ミニマップといった操作系と描画を丸ごと提供するので、自前でSVGのドラッグ処理を書く必要がありません。
使われ方の例として、公式サイトはStripe Docs(処理の流れの図示)やTypeform(アンケート分岐ロジックの可視化)を挙げています。国内ではCyberAgentのAI Workerが、ワークフロー編集画面をRete.jsからReact Flowへ移した事例を公開しており、社内アンケートで集まったUI/UXの改善要望69件(とくにズーム操作の精度)と、Rete.jsのReact用プラグインがstyled-componentsに依存していた点を移行理由に挙げています。移行は当初見込みの約1か月に対して約2週間で終わったとのことです(CyberAgent Developers Blog)。
パッケージ名の変遷と現行版12.12.0
React Flowはパッケージ名が2回変わっており、検索で古いコードにぶつかる原因になっています。npmレジストリで確認した各パッケージの最終版は次のとおりです(2026年10月19日時点)。
| パッケージ | 系列 | 最終版 | 公開日 | 状態 |
|---|---|---|---|---|
| react-flow-renderer | v10以前 | 10.3.17 | 2022-09-14 | npm上で非推奨表示 |
| reactflow | v11 | 11.11.4 | 2024-06-20 | 更新停止 |
| @xyflow/react | v12 | 12.12.0 | 2026-09-24 | 現行 |
| @xyflow/react | v13 | 13.0.0-next.0 | 2026-10-11 | プレリリース |
npmの集計APIで取得した直近1週間(10月10日〜16日)のダウンロード数は @xyflow/react が約835万回、旧名の reactflow も約124万回あり、旧パッケージのダウンロードも続いていることが分かります。ただし、CIなどの反復取得も含むため、稼働案件数を直接示す値ではありません。12系は小刻みに機能が増えており、12.9.0でエッジにツールバーを付ける EdgeToolbar、12.10.0で重なり順を制御する zIndexMode、12.11.0で範囲選択中に画面端へ寄せると自動でスクロールする autoPanOnSelection が入りました。12.12.0はリサイズ時に onResizeEnd が必ず onResizeStart の後に発火するようにした変更です(GitHubのリリースノート)。
PythonやVue・Svelteでの利用可否
React FlowはReactのコンポーネントなので、Python単体やVue単体では動きません。Django・FastAPIなどと組み合わせる場合は、画面をReactで作り、Python側はノードとエッジのJSONを保存・配信するAPIを受け持つ形になります。
Svelteなら同じxyflowチームが保守するSvelte Flow(@xyflow/svelte、12.12.0と同日に1.7.0)があり、内部の計算部分は共通パッケージ @xyflow/system で共有しています。VueのVue Flow(@vue-flow/core、最新1.48.2)は、READMEに「heavily based on ReactFlow」とある別プロジェクトです。APIの考え方は似ていても、React Flowのコードはそのままでは動きません。
React Flowのライセンス:MIT・帰属表示・Proの関係
React Flow本体はMITライセンスです。商用製品への組み込み、改変、再配布、販売まで無料でできます。義務は、本ソフトウェアのコピーまたは実質的な部分に著作権表示と許諾表示を含めることで、ソースの再配布に限らずビルド済みのJavaScriptに同梱して配る場合も対象になり得ます。社内システムでもSaaSでも、使うだけなら費用はかかりません。
迷いやすいのは画面右下に出る「React Flow」の帰属表示(attribution)です。これは proOptions の hideAttribution で消せます。
<ReactFlow nodes={nodes} edges={edges} proOptions={{ hideAttribution: true }} />
技術的には誰でも消せますが、公式のProOptionsのリファレンスは「attribution visible, no subscription needed; no attribution, please subscribe.」と方針を示しています。12.12.0のソースでは、帰属表示を隠した状態で開発ビルドを動かすと「Please only do this when you are subscribed to React Flow Pro」という警告をコンソールに出す処理が入っています(12.11.4で追加)。MITなので法的な禁止ではありませんが、帰属表示を消すならProに加入する、というのが公式の求める線引きです。
Proの料金は公式のPro紹介ページで月払いの場合次のように表示されています。年払いの金額は月払いと切り替えて確認してください。
| プラン | 月額(月払い) | 招待人数 | 個別サポート |
|---|---|---|---|
| Starter | 169ドル | 1人 | なし |
| Professional | 289ドル | 5人 | メールで月1時間まで |
| Enterprise | 個別見積もり | 10人 | 音声・動画・メールで月1時間 |
どのプランにもPro ExamplesとTemplates(自動レイアウトや共同編集などの有料サンプル)へのアクセスと、GitHub Issueの優先対応が付きます。ライブラリの機能自体はProでも無料版でも同じです。帰属表示を残せるなら加入は不要、と判断して構いません。
React Flowのインストールと最小構成
必要なのはパッケージの追加とCSSの読み込みです。CSSを読み込まないとノードの枠やエッジの線が表示されません。
npm install @xyflow/react
最小構成は、ノード配列・エッジ配列と、ノード変更・エッジ変更・接続の3つのハンドラでできています。親の <div> に高さを指定しないと高さ0で何も見えない点に注意してください。
import { useState, useCallback } from 'react';
import {
ReactFlow,
Background,
Controls,
MiniMap,
addEdge,
applyNodeChanges,
applyEdgeChanges,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{ id: '1', type: 'input', position: { x: 0, y: 0 }, data: { label: '申請' } },
{ id: '2', position: { x: 0, y: 120 }, data: { label: '承認' } },
];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2', animated: true }];
export default function FlowChart() {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((ns) => applyNodeChanges(changes, ns)),
[],
);
const onEdgesChange = useCallback(
(changes) => setEdges((es) => applyEdgeChanges(changes, es)),
[],
);
const onConnect = useCallback(
(params) => setEdges((es) => addEdge(params, es)),
[],
);
return (
<div style={{ width: '100%', height: '500px' }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
>
<Background />
<Controls />
<MiniMap />
</ReactFlow>
</div>
);
}
fitView は初期表示で全ノードを画面に収めるプロパティです。ノードのドラッグと、ハンドルからハンドルへのドラッグによる接続は、3つのハンドラを渡した時点で有効になります。useState と applyNodeChanges の組み合わせは、useNodesState/useEdgesState というフックで短く書くこともできます。
ReactFlowProviderが必要になる場面
<ReactFlow> を1つ置くだけなら ReactFlowProvider は要りません。必要になるのは、useReactFlow などのフックを <ReactFlow> の外側(ツールバーや保存ボタンなど)で使うときです。その場合は両方を ReactFlowProvider で包みます。
import { ReactFlow, ReactFlowProvider, useReactFlow } from '@xyflow/react';
function Toolbar() {
// Provider の外で呼ぶとエラーになる
const { fitView, zoomIn } = useReactFlow();
return (
<div>
<button onClick={() => zoomIn()}>拡大</button>
<button onClick={() => fitView()}>全体表示</button>
</div>
);
}
export default function App() {
return (
<ReactFlowProvider>
<Toolbar />
<div style={{ width: '100%', height: '500px' }}>
<ReactFlow nodes={[]} edges={[]} fitView />
</div>
</ReactFlowProvider>
);
}
TypeScriptによるカスタムノードの型付け
見た目の作り込みは、自分のReactコンポーネントをノードとして登録して行います。接続点は Handle、その位置は Position で指定し、作ったコンポーネントを nodeTypes に登録してノードの type にその名前を書きます。TypeScriptでは Node<データの型, 種別名> でノードの型を作り、NodeProps に渡すと data の中身まで型が付きます。
import { memo } from 'react';
import { ReactFlow, Handle, Position, type Node, type NodeProps } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
// data の型とノード種別名をセットで定義する
type TaskNode = Node<{ label: string; owner: string }, 'task'>;
const TaskNodeView = memo(({ data }: NodeProps<TaskNode>) => (
<div style={{ padding: 10, border: '1px solid #4051b5', borderRadius: 6, background: '#fff' }}>
<Handle type="target" position={Position.Top} />
<strong>{data.label}</strong>
<div>担当:{data.owner}</div>
<Handle type="source" position={Position.Bottom} />
</div>
));
// 再生成を避けるためコンポーネントの外で定義する
const nodeTypes = { task: TaskNodeView };
const nodes: TaskNode[] = [
{ id: '1', type: 'task', position: { x: 0, y: 0 }, data: { label: '上長承認', owner: '課長' } },
];
export default function App() {
return (
<div style={{ width: '100%', height: '400px' }}>
<ReactFlow nodes={nodes} edges={[]} nodeTypes={nodeTypes} fitView />
</div>
);
}
このコードは tsc --strict で型エラーが出ないことを確認しています。nodeTypes をコンポーネント内で毎回作ると、描画のたびにノードが作り直されて開発時に警告が出ます。コンポーネントの外に置くか、useMemo で固定してください。
見た目を一から作りたくない場合は、公式のReact Flow UIが使えます。shadcn/uiとTailwind CSSで作られた部品集で、2025年7月にReact Flow Componentsから改称されました。shadcn/uiとTailwind CSSを設定したうえで、たとえば npx shadcn@latest add https://ui.reactflow.dev/base-node を実行すると部品のソースが自分のプロジェクトへコピーされる方式なので、取り込んだ後は自由に書き換えられます。
toObjectによるフローのJSON保存・復元
作った図を保存して開き直すには、インスタンスの toObject() を使います。戻り値はノード・エッジ・ビューポート(表示位置とズーム倍率)をまとめたオブジェクトなので、ノードやエッジのデータをJSONで表現できる値にそろえておけば、JSON.stringify してサーバーやlocalStorageに保存できます。関数やReact要素などは保存対象のデータから分離します。次のツールバーは、対象のフローと同じ ReactFlowProvider の内側に置きます。制御モードでは、フロー側に onNodesChange と onEdgesChange を渡して親の状態を更新する必要があります。
import { useCallback } from 'react';
import { useReactFlow } from '@xyflow/react';
// ReactFlowProvider の内側に置くツールバー
export function SaveRestore() {
const { toObject, setNodes, setEdges, setViewport } = useReactFlow();
const onSave = useCallback(() => {
const flow = toObject(); // { nodes, edges, viewport }
localStorage.setItem('flow', JSON.stringify(flow));
}, [toObject]);
const onRestore = useCallback(() => {
const raw = localStorage.getItem('flow');
if (!raw) return;
const flow = JSON.parse(raw);
setNodes(flow.nodes ?? []);
setEdges(flow.edges ?? []);
const { x = 0, y = 0, zoom = 1 } = flow.viewport ?? {};
setViewport({ x, y, zoom });
}, [setNodes, setEdges, setViewport]);
return (
<div>
<button onClick={onSave}>保存</button>
<button onClick={onRestore}>復元</button>
</div>
);
}
実際に保存したJSONを見ると、各ノードには id・type・position・data に加えて、描画後に測った寸法の measured(担当者名を2行で表示するカスタムノードでは {"width":86,"height":70})が入っていました。復元時にこの値が残っていても問題なく描画されます。ノード全体を消したあとに復元ボタンを押し、4ノード・3エッジが戻ることも確認しています。親で useNodesState を使う制御モードでも、useReactFlow の setNodes で復元できます。
大きなフローで詰まる点:自動レイアウトとSSR
ノード数や接続関係が頻繁に変わるフローでは、座標の手動調整が増えるため、自動レイアウトを検討します。またNext.jsなどでサーバー側描画をすると、ノードは出るのに線が出ない、という問題に当たります。自動レイアウトでは座標計算を外部ライブラリに任せ、SSRでは描画に必要なノード寸法とハンドル位置を事前に渡します。
dagreによるノードの自動配置
先に npm install @dagrejs/[email protected] で、以下の例に必要な依存パッケージを追加します。React Flow自体はレイアウト計算を持たないので、外部ライブラリで座標を求めて position に入れます。公式のレイアウトのガイドは、木構造に並べるなら「we highly recommend dagre」としています。d3-hierarchyは根が1つのグラフ専用、elkjsは設定項目が多く複雑なのであまり勧めない、という位置づけです。なおelkjsのライセンスはEPL-2.0またはGPL-3.0で、MITのReact Flowと揃えたい場合にも検討が要ります。
import dagre from '@dagrejs/dagre';
const NODE_W = 172;
const NODE_H = 60;
// nodes と edges を受け取り、上から下へ並べた位置を返す
export function layout(nodes, edges, rankdir = 'TB') {
const g = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
g.setGraph({ rankdir, nodesep: 40, ranksep: 60 });
nodes.forEach((n) => g.setNode(n.id, { width: NODE_W, height: NODE_H }));
edges.forEach((e) => g.setEdge(e.source, e.target));
dagre.layout(g);
// dagre は中心座標、React Flow は左上座標なので半分ずらす
return nodes.map((n) => {
const p = g.node(n.id);
return { ...n, position: { x: p.x - NODE_W / 2, y: p.y - NODE_H / 2 } };
});
}
@dagrejs/dagre 3.1.1で、4ノード(a→b、a→c、c→d)を上から下へ並べることを確認しました。注意点は、dagreに渡す幅と高さが仮の値だということです。実際の寸法は描画後に node.measured に入るので、ノードの大きさがまちまちなら、初回描画の後に measured の幅・高さを各ノードのレイアウト入力に使い、中心座標から左上座標へ変換する際も各ノードの寸法の半分を引きます。サブフローを含む場合などは、配置結果の重なりを別途確認してください。
SSRでエッジまで描画する条件
サーバー側描画はv12から対応しています(SSR/SSGの設定ガイド)。ブラウザと違ってサーバーではDOMを測れないため、ノードの width/height と、エッジをつなぐハンドルの位置 handles を自分で渡します。
import { ReactFlow, Position } from '@xyflow/react';
// サーバー側では DOM を測れないので、寸法とハンドル位置を自分で渡す
const nodes = [
{
id: '1', position: { x: 0, y: 0 }, data: { label: 'A' }, width: 150, height: 40,
handles: [{ type: 'source', position: Position.Bottom, x: 75, y: 40 }],
},
{
id: '2', position: { x: 0, y: 120 }, data: { label: 'B' }, width: 150, height: 40,
handles: [{ type: 'target', position: Position.Top, x: 75, y: 0 }],
},
];
const edges = [{ id: 'e1-2', source: '1', target: '2' }];
export default function StaticFlow() {
return <ReactFlow nodes={nodes} edges={edges} width={400} height={300} fitView />;
}
同じ2ノード1エッジを renderToStaticMarkup で文字列にして比べると、寸法とハンドルを渡した版はノード2つとエッジの線1本が出力されました。渡さない版はノードが visibility:hidden になり、エッジの線は1本も出ませんでした。ログイン不要の公開ページで図をHTMLに含めたい場合は、この2つを必ず指定します。
旧バージョンからの移行とv13の変化
ネット上のReact Flow解説には react-flow-renderer 時代のものが多く、そのまま写すとv12では動きません。公式のv10移行ガイドとv12移行ガイドに沿って、つまずきやすい変更を表にしました。
v9以前・v10・v11からv12への移行箇所
| 旧(v9以前・v10・v11) | 移行先 |
|---|---|
import ReactFlow from 'reactflow' |
import { ReactFlow } from '@xyflow/react' |
| elements(v9以前の単一配列) | nodes と edges の別配列(v10以降) |
| removeElements(v9以前) | applyNodeChanges/applyEdgeChanges(v10以降) |
| onLoad(v9以前) | onInit(v10以降) |
| onEdgeUpdate | onReconnect(v12) |
| node.parentNode | node.parentId(v12) |
| node.width/height(測定値) | node.measured.width/height(v12) |
v9以前から移行する場合は、v10で廃止された elements をノードとエッジの別配列に変更するのが最大の作業です。ノードとエッジを1つの配列に混ぜて removeElements で消していた書き方を、2つの配列と changes 方式のハンドラに書き換えます。v11からなら、importの変更と measured・parentId・onReconnect への置き換えが中心です。v12では node.width が「測った値」ではなく「指定する寸法」に意味が変わったため、寸法を読み取るコードは measured に直さないと値がずれます。
13.0.0-next.0で変わる前提
2026年10月11日に @xyflow/[email protected] がnpmに next タグで公開されました。変更履歴はまだ公開されていませんが、12.12.0と package.json を比べると前提の変化が読み取れます。
| 項目 | 12.12.0 | 13.0.0-next.0 |
|---|---|---|
| 対応React(peer) | 17以上 | 18以上 |
| 配布形式 | ESM+UMD(require可) | ESMのみ |
| Node.js(engines) | 指定なし | 20以上 |
| zustand | 4系 | 5系 |
| react-compiler-runtime | なし | 依存に追加 |
React 17のまま運用している案件は、v13の正式版で最低要件を満たさなくなります。CommonJSの require で読み込んでいる環境(古いJestの設定など)は、Node.jsやテストランナーのESM対応によって読み込めるかどうかが変わるため、事前の確認が必要です。プレリリースなので今は本番に入れず、React 18以上への更新とESM化を先に済ませておくのが安全です。
React Flowが向く場面と他ライブラリとの選び分け
判断の軸は「利用者が図を編集するか、図を見せるだけか」です。Reactで作るワークフロービルダーやノードエディタで、ノードの追加・接続・移動が必要なら、React Flowを第一候補にします。承認ルートを画面で組み立てる業務アプリのように、ワークフローシステム開発の中で分岐の編集画面が要る場面は典型的な用途です。
テキストで定義した静的な図を表示する用途なら、まずMermaidを選びます(VSCodeでMermaidを使う方法)。表示専用でも、独自のReactコンポーネントをノードに埋め込む必要がある場合はReact Flowが候補になります。手書き風のホワイトボードが欲しいならExcalidraw、図ではなく箱のドラッグとリサイズだけが要るなら react-rndでのドラッグ&リサイズ で足ります。
| ライブラリ | 対応フレームワーク | ライセンス | 向く用途 |
|---|---|---|---|
| React Flow | React | MIT | 編集できるノードUI |
| Svelte Flow | Svelte | MIT | 同上(Svelte) |
| Vue Flow | Vue 3 | MIT | 同上(Vue) |
| Rete.js 2.0.6 | React・Vue・Angular等 | MIT | データ処理エディタ |
| Mermaid | 不問(テキスト記法) | MIT | 表示専用の図 |
Rete.jsは複数フレームワークに対応する代わりに、Reactで描画するには rete-react-plugin などのプラグインを組み合わせ、そのプラグインがstyled-componentsを必要とします。Tailwind CSSで統一しているReactプロジェクトなら、前述のCyberAgentの事例のようにReact Flowのほうが依存を減らせます。フロントがReactでない場合は、ページの一部だけReactにする AstroでReactを使う方法 のような構成でReact Flowを載せる手もあります。
よくある質問
React Flowは商用利用できますか?
できます。MITライセンスなので、有料の製品やSaaSに組み込んでも費用はかかりません。右下の帰属表示を消す場合だけ、公式はPro(月額169ドルから)への加入を求めています。
reactflow と @xyflow/react は何が違いますか?
同じライブラリの旧名と現行名です。reactflow はv11(最終11.11.4)、@xyflow/react はv12以降です。v12ではimportが名前付きになり、測定した寸法が node.measured に移るなど、APIも一部変わっています。
React FlowはPythonで使えますか?
Python単体では使えません。画面はReactで作り、DjangoやFastAPIはノードとエッジのJSONを保存・配信するバックエンドとして組み合わせます。
TypeScriptで使えますか?
使えます。パッケージに型定義が含まれており、Node<データの型, 種別名> と NodeProps を使うと、カスタムノードの data まで型が付きます。
React 17や18でも動きますか?
12.12.0は peerDependencies が react >=17 なので、17・18・19で使えます。13.0.0-next.0では18以上に引き上げられているため、17の案件はv12にとどまるかReactの更新が必要です。
右クリックメニューは付けられますか?
付けられます。onNodeContextMenu(ノード上)や onPaneContextMenu(背景)でイベントを受け取り、メニューの表示位置と中身は自分で実装します。既製のメニュー部品はありません。