mcpField
R
react-flow

react-flow

github.com/existential-birds開発851.3k 回インストール11 週間前に更新
リポジトリ

react-flow について

react-flowは、Reactアプリでノードグラフ、ワークフローエディター、インタラクティブな図を構築するライブラリです。カスタムノードやエッジ、イベント処理、ビューポート制御を備え、ビジュアルプログラミングやプロセスフローに利用できます。

主な機能

  1. 01ノードとエッジによるグラフ構築
  2. 02ノードと接続のカスタマイズ
  3. 03グラフ状態の制御
  4. 04選択、ドラッグ、接続イベントの処理
  5. 05ビューポートと座標変換の制御
  6. 06接続検証とノード更新

活用シーン

  1. 01ビジュアルワークフローエディターの構築
  2. 02ノードネットワークやプロセス図の表示
  3. 03インタラクティブなビジュアルプログラミング画面の作成
  4. 04ユーザー操作に応じたグラフ構造の更新

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/existential-birds/beagle --skill react-flow

インストール後、Claude Code で/react-flowを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
custom-edges.md
custom-nodes.md
events.md
viewport.md

詳細

React Flow

React Flow(@xyflow/react)は、ノードベースのグラフ、ワークフローエディター、インタラクティブな図を構築するためのライブラリです。ビジュアルプログラミングインターフェース、プロセスフロー、ネットワークの可視化を作成するための、高度にカスタマイズ可能なフレームワークを提供します。

クイックスタート

インストール
pnpm add @xyflow/react
基本設定
import { ReactFlow, Node, Edge, Background, Controls, MiniMap } from '@xyflow/react';
import '@xyflow/react/dist/style.css';

const initialNodes: Node[] = [
  {
    id: '1',
    type: 'input',
    data: { label: 'Input Node' },
    position: { x: 250, y: 5 },
  },
  {
    id: '2',
    data: { label: 'Default Node' },
    position: { x: 100, y: 100 },
  },
  {
    id: '3',
    type: 'output',
    data: { label: 'Output Node' },
    position: { x: 400, y: 100 },
  },
];

const initialEdges: Edge[] = [
  { id: 'e1-2', source: '1', target: '2', animated: true },
  { id: 'e2-3', source: '2', target: '3' },
];

function Flow() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ReactFlow nodes={initialNodes} edges={initialEdges}>
        <Background />
        <Controls />
        <MiniMap />
      </ReactFlow>
    </div>
  );
}

export default Flow;

コアコンセプト

ノード

ノードはグラフを構成する基本要素です。各ノードには次の要素があります。

  • id:一意の識別子
  • type:ノードの種類(組み込みまたはカスタム)
  • position:{ x, y } 座標
  • data:カスタムデータオブジェクト
import { Node } from '@xyflow/react';

const node: Node = {
  id: 'node-1',
  type: 'default',
  position: { x: 100, y: 100 },
  data: { label: 'Node Label' },
  style: { background: '#D6D5E6' },
  className: 'custom-node',
};

組み込みのノードタイプ:

  • default:標準ノード
  • input:ターゲットハンドルなし
  • output:ソースハンドルなし
  • group:ほかのノードを格納するコンテナー
エッジ

エッジはノードを接続します。各エッジには次の要素が必要です。

  • id:一意の識別子
  • source:ソースノードの ID
  • target:ターゲットノードの ID
import { Edge } from '@xyflow/react';

const edge: Edge = {
  id: 'e1-2',
  source: '1',
  target: '2',
  type: 'smoothstep',
  animated: true,
  label: 'Edge Label',
  style: { stroke: '#fff', strokeWidth: 2 },
};

組み込みのエッジタイプ:

  • default:ベジェ曲線
  • straight:直線
  • step:角が鋭い直交線
  • smoothstep:角が丸い直交線
ハンドル

ハンドルはノード上の接続ポイントです。配置には Position 列挙型を使用します。

import { Handle, Position } from '@xyflow/react';

<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />

使用可能な位置:Position.Top、Position.Right、Position.Bottom、Position.Left

状態管理

制御フロー

状態フックを使用して完全に制御します。

import { useNodesState, useEdgesState, addEdge, OnConnect } from '@xyflow/react';
import { useCallback } from 'react';

function ControlledFlow() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

  const onConnect: OnConnect = useCallback(
    (connection) => setEdges((eds) => addEdge(connection, eds)),
    [setEdges]
  );

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
    />
  );
}
useReactFlow フック

React Flow インスタンスにアクセスして、プログラムから制御します。

import { useReactFlow } from '@xyflow/react';

function FlowControls() {
  const {
    getNodes,
    getEdges,
    setNodes,
    setEdges,
    addNodes,
    addEdges,
    deleteElements,
    fitView,
    zoomIn,
    zoomOut,
    getNode,
    getEdge,
    updateNode,
    updateEdge,
  } = useReactFlow();

  return (
    <button onClick={() => fitView()}>Fit View</button>
  );
}

カスタムノード

型付きデータを使って NodeProps<T> でカスタムノードを定義します。

import { NodeProps, Node, Handle, Position } from '@xyflow/react';

export type CustomNode = Node<{ label: string; status: 'active' | 'inactive' }, 'custom'>;

function CustomNodeComponent({ data, selected }: NodeProps<CustomNode>) {
  return (
    <div className={`px-4 py-2 ${selected ? 'ring-2' : ''}`}>
      <Handle type="target" position={Position.Top} />
      <div className="font-bold">{data.label}</div>
      <Handle type="source" position={Position.Bottom} />
    </div>
  );
}

nodeTypes に登録します。

const nodeTypes: NodeTypes = { custom: CustomNodeComponent };
<ReactFlow nodeTypes={nodeTypes} />
主なパターン
  • 複数のハンドル:配置には id プロパティと style を使用します
  • 動的ハンドル:ハンドルの追加/削除後に useUpdateNodeInternals([nodeId]) を呼び出します
  • インタラクティブ要素:入力欄やボタン上でのドラッグを防ぐには、className="nodrag" を追加します

スタイル設定、航空地図ピン、動的ハンドルなどの詳しいパターンについては、カスタムノードのリファレンスを参照してください。

カスタムエッジ

EdgeProps<T> とパスユーティリティを使ってカスタムエッジを定義します。

import { BaseEdge, EdgeProps, getBezierPath } from '@xyflow/react';

export type CustomEdge = Edge<{ status: 'normal' | 'error' }, 'custom'>;

function CustomEdgeComponent(props: EdgeProps<CustomEdge>) {
  const [edgePath] = getBezierPath(props);

  return (
    <BaseEdge
      id={props.id}
      path={edgePath}
      style={{ stroke: props.data?.status === 'error' ? '#ef4444' : '#64748b' }}
    />
  );
}
パスユーティリティ
  • getBezierPath() - 滑らかな曲線
  • getStraightPath() - 直線
  • getSmoothStepPath() - 角が丸い直交線
  • getSmoothStepPath({ borderRadius: 0 }) - 角が鋭い直交線(step エッジ)

いずれも [path, labelX, labelY, offsetX, offsetY] を返します。

インタラクティブなラベル

ポインターイベントに対応した HTML ベースのラベルには、EdgeLabelRenderer を使用します。

import { EdgeLabelRenderer, BaseEdge, getBezierPath } from '@xyflow/react';

function ButtonEdge(props: EdgeProps) {
  const [edgePath, labelX, labelY] = getBezierPath(props);
  return (
    <>
      <BaseEdge id={props.id} path={edgePath} />
      <EdgeLabelRenderer>
        <div
          style={{
            position: 'absolute',
            transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
            pointerEvents: 'all',
          }}
          className="nodrag nopan"
        >
          <button onClick={() => console.log('Delete')}>×</button>
        </div>
      </EdgeLabelRenderer>
    </>
  );
}

アニメーション付きエッジ、時間ラベル、SVG テキストのパターンについては、カスタムエッジのリファレンスを参照してください。

ビューポートの制御

プログラムからビューポートを制御するには、useReactFlow() フックを使用します。

import { useReactFlow } from '@xyflow/react';

function ViewportControls() {
  const { fitView, zoomIn, zoomOut, setCenter, screenToFlowPosition } = useReactFlow();

  // Fit all nodes in view
  const handleFitView = () => fitView({ padding: 0.2, duration: 400 });

  // Zoom controls
  const handleZoomIn = () => zoomIn({ duration: 300 });
  const handleZoomOut = () => zoomOut({ duration: 300 });

  // Center on specific coordinates
  const handleCenter = () => setCenter(250, 250, { zoom: 1.5, duration: 500 });

  // Convert screen coordinates to flow coordinates
  const addNodeAtClick = (event: React.MouseEvent) => {
    const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });
    // Use position to add node
  };

  return null;
}

状態の保存/復元、制御されたビューポート、座標変換については、ビューポートのリファレンスを参照してください。

イベント

React Flow は包括的なイベント処理を提供します。

ノードイベント
import { NodeMouseHandler, OnNodeDrag } from '@xyflow/react';

const onNodeClick: NodeMouseHandler = (event, node) => {
  console.log('Node clicked:', node.id);
};

const onNodeDrag: OnNodeDrag = (event, node, nodes) => {
  console.log('Dragging:', node.id);
};

<ReactFlow
  onNodeClick={onNodeClick}
  onNodeDrag={onNodeDrag}
  onNodeDragStop={onNodeClick}
/>
エッジと接続のイベント
import { EdgeMouseHandler, OnConnect } from '@xyflow/react';

const onEdgeClick: EdgeMouseHandler = (event, edge) => console.log('Edge:', edge.id);
const onConnect: OnConnect = (connection) => console.log('Connected:', connection);

<ReactFlow onEdgeClick={onEdgeClick} onConnect={onConnect} />
選択とビューポートのイベント
import { useOnSelectionChange, useOnViewportChange } from '@xyflow/react';

useOnSelectionChange({
  onChange: ({ nodes, edges }) => console.log('Selected:', nodes.length, edges.length),
});

useOnViewportChange({
  onChange: (viewport) => console.log('Viewport:', viewport.zoom),
});

検証、削除、エラー処理を含むイベント一覧については、イベントのリファレンスを参照してください。

一般的なパターン

ドラッグ/パンの防止
<input className="nodrag" />
<button className="nodrag nopan">Click me</button>
接続の検証
const isValidConnection = (connection: Connection) => {
  return connection.source !== connection.target; // Prevent self-connections
};

<ReactFlow isValidConnection={isValidConnection} />
クリック時にノードを追加
const { screenToFlowPosition, setNodes } = useReactFlow();

const onPaneClick = (event: React.MouseEvent) => {
  const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });
  setNodes(nodes => [...nodes, { id: `node-${Date.now()}`, position, data: { label: 'New' } }]);
};
ノードデータの更新
const { updateNodeData } = useReactFlow();
updateNodeData('node-1', { label: 'Updated' });
updateNodeData('node-1', (node) => ({ ...node.data, count: node.data.count + 1 }));

プロバイダーパターン

フローの外部で useReactFlow() を使用する場合は、アプリを ReactFlowProvider でラップします。

import { ReactFlow, ReactFlowProvider, useReactFlow } from '@xyflow/react';

function Controls() {
  const { fitView } = useReactFlow(); // Must be inside provider
  return <button onClick={() => fitView()}>Fit View</button>;
}

function App() {
  return (
    <ReactFlowProvider>
      <Controls />
      <ReactFlow nodes={nodes} edges={edges} />
    </ReactFlowProvider>
  );
}

実装時の確認事項

統合が完了したと判断する前に、以下の順序に従ったチェックを実施してください(一般的な落とし穴を対象としており、スタイル上の好みは対象外です)。

  1. バンドル内の CSS — アプリ内(エントリーまたはレイアウト)で import '@xyflow/react/dist/style.css' が実行されることを確認してください。**合格条件:**ノードとエッジに想定どおりのデフォルトスタイルが適用され、ハンドルが表示されて操作できます。
  2. 安定した nodeTypes / edgeTypes — レンダーごとに新しいオブジェクトリテラルを渡さないでください。マップをコンポーネントの外で定義するか、依存配列を正しく指定して useMemo でメモ化してください。**合格条件:**選択状態またはビューポートだけが変化したときに、再マウントによるちらつきや、「maximum update depth」エラー、更新の暴走が発生しません。
  3. プロバイダーの境界 — useReactFlow() を呼び出すコンポーネントは ReactFlowProvider の子孫であり、フローが実際にマウントされている必要があります。**合格条件:**実行時にコンテキスト不足のエラーが発生せず、想定される場所でプログラムによる API(fitView など)が動作します。

参考ファイル

実装パターンの詳細については、以下を参照してください。

  • カスタムノード - NodeProps の型指定、Handle コンポーネント、動的ハンドル、スタイル設定パターン
  • カスタムエッジ - EdgeProps の型指定、パスユーティリティ、EdgeLabelRenderer、アニメーション付きエッジ
  • ビューポート - useReactFlow のメソッド、fitView のオプション、座標変換
  • イベント - ノード/エッジ/接続のイベント、選択状態の処理、ビューポートの変更

コメント