react-flow-v12-setup
ワークフローエディタ / ER 図 / マインドマップ / DAG ビジュアライザ等の **ノードベース UI** を React で実装する時に呼び出すべき skill。React Flow v12(`@xyflow/react`)で最小コードで立ち上げ、カスタムノード追加、プログラマティックなノード/エッジ追加削除、localStorage 永続化、TypeScript 型(`Node<T>` / `Edge`)、よく詰まる落とし穴(`React.memo` / `useNodesState` 等)までを実装ベースで解説。次のいずれかに該当する時に invoke する: (1) ノード/エッジ/ハンドルが出てくる UI 要件、(2) `@xyflow/react` や `reactflow` の import を含むファイルを編集中、(3) ユーザーが 'flow chart' / 'node editor' / 'graph editor' を作りたいと言及、(4) `<ReactFlow />` の使い方 / 型 / 落とし穴に関する質問。検証バージョン: 12.10.1、検証日: 2026-05-09。
React Flow v12(@xyflow/react)実装 skill
検証バージョン:
@xyflow/react@12.10.1検証日: 2026-05-09 対象: React 19 + TypeScript で@xyflow/reactを使う実装作業
ノードベース UI(ワークフロー / マインドマップ / ER 図 / DAG エディタ)を React で構築する時の実装パターン。
まず詰まる 3 点
実装着手前に確認:
- コンテナに
widthとheightを明示しないと描画されない(0px 扱い)。100vhか親要素の固定高さが必要 @xyflow/react/dist/style.cssの import 必須。これが無いとハンドル(接続点)が見えない- node の
idは string 必須。数値や undefined を渡すと黙ってバグる。crypto.randomUUID()などで明示文字列化
最小サンプル(コピペで動く)
import {
ReactFlow,
ReactFlowProvider,
useNodesState,
useEdgesState,
Controls,
Background,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
const initialNodes = [
{ id: "1", position: { x: 0, y: 0 }, data: { label: "入力" } },
{ id: "2", position: { x: 200, y: 100 }, data: { label: "処理" } },
];
function FlowInner() {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, , onEdgesChange] = useEdgesState([]);
return (
<div style={{ width: "100vw", height: "100vh" }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
fitView
>
<Controls />
<Background />
</ReactFlow>
</div>
);
}
export default function Flow() {
return (
<ReactFlowProvider>
<FlowInner />
</ReactFlowProvider>
);
}
ReactFlowProvider で囲むと useReactFlow() が使える。つねにラップする クセをつける(後で View 操作したくなる)。
カスタムノード(Handle で接続点を定義)
import { Handle, Position, type NodeProps } from "@xyflow/react";
type MyData = { label: string; status: "ok" | "warn" | "ng" };
export function MyNode({ data }: NodeProps<MyData>) {
return (
<div className="rounded border p-2 bg-white">
<Handle type="target" position={Position.Top} />
<strong>{data.label}</strong>
<span data-status={data.status}>{data.status}</span>
<Handle type="source" position={Position.Bottom} />
</div>
);
}
const nodeTypes = { my: MyNode };
// <ReactFlow nodeTypes={nodeTypes} ... />
nodeTypes は memo 化が暗黙の前提。コンポーネント外で定義する(関数内で {} を毎回 new するとレンダループに入る)。
プログラマティックに追加 / 削除
import { useReactFlow } from "@xyflow/react";
function ToolBar() {
const { addNodes, deleteElements, getNodes } = useReactFlow();
const add = () =>
addNodes({
id: crypto.randomUUID(),
position: { x: Math.random() * 400, y: Math.random() * 400 },
data: { label: "new" },
});
const removeSelected = () => {
const selected = getNodes().filter((n) => n.selected);
deleteElements({ nodes: selected });
};
return (
<>
<button onClick={add}>追加</button>
<button onClick={removeSelected}>選択を削除</button>
</>
);
}
useReactFlow() は ReactFlowProvider 内でしか動かない。プロバイダ外で叩くと無音で失敗する。
ローカル永続化(localStorage)
const { toObject } = useReactFlow();
const onSave = useCallback(() => {
localStorage.setItem("my-flow", JSON.stringify(toObject()));
}, [toObject]);
const onRestore = useCallback(() => {
const raw = localStorage.getItem("my-flow");
if (!raw) return;
const flow = JSON.parse(raw);
setNodes(flow.nodes ?? []);
setEdges(flow.edges ?? []);
setViewport(flow.viewport);
}, [setNodes, setEdges, setViewport]);
toObject() は viewport も含む。setViewport(flow.viewport) で復元しないとカメラ位置が保存されない。
TypeScript の型
Node / Edge は generic で data の型を絞り込める。
import type { Node, Edge } from "@xyflow/react";
type StepData = { label: string; status: "ok" | "warn" | "ng" };
type StepNode = Node<StepData, "step">;
type FlowEdge = Edge;
const initialNodes: StepNode[] = [
{ id: "1", type: "step", position: { x: 0, y: 0 }, data: { label: "...", status: "ok" } },
];
type: "step" を Node<Data, Type> の第 2 引数で固定しておくと、nodeTypes.step の component が必ず NodeProps<StepData> を受ける関係になる。
バージョン間の罠(古い情報を掴みやすい)
- v11 以前の
reactflowパッケージ は別物。検索で出てくる古い記事は v11 系であることが多く、import が違う(from 'reactflow'vsfrom '@xyflow/react') - v11 の
reactflow/dist/style.cssも別パス useStoreの API は v12 で再設計されている。v11 のコードをコピペしてもしばしば動かない
検索する時は @xyflow/react で絞る(reactflow だと v11 系も混ざる)。
パフォーマンス
| ノード数 | 体感 | 対策 |
|---|---|---|
| 〜数百 | 余裕 | デフォルトで OK |
| 〜数千 | 操作で重い | onlyRenderVisibleElements を true、ノードを memo 化 |
| 1 万〜 | 厳しい | 別ライブラリ(Cytoscape / Sigma)検討 or 仮想化 |
data を mutate すると React Flow が変更を検知できない(参照が同じだから)。必ず新しいオブジェクトを作る({ ...prev, label: "new" })。
向く / 向かないケース
- 向く: ワークフローエディタ、ER 図、データフロー UI、設定 UI、マインドマップ
- 向かない: 1 万ノード級の可視化、純粋な静的図(Mermaid で十分)、複雑なグラフ計算(d3-force と組み合わせる必要)
See also(任意、単独でも完結)
本 skill は React Flow の実装に特化しており、単独で完結します。記事化する場合は本リポジトリの公開版編集方針 src/content/methodology/01-editorial-principles を参照。
参考
- 公式 doc: https://reactflow.dev
- npm:
@xyflow/react - GitHub: https://github.com/xyflow/xyflow