react-flow

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Flow

React Flow

React Flow (@xyflow/react) is a library for building node-based graphs, workflow editors, and interactive diagrams. It provides a highly customizable framework for creating visual programming interfaces, process flows, and network visualizations.
React Flow (@xyflow/react) 是一个用于构建基于节点的图形、工作流编辑器和交互式图表的库。它提供了高度可定制的框架,用于创建可视化编程界面、流程流和网络可视化。

Quick Start

快速开始

Installation

安装

bash
pnpm add @xyflow/react
bash
pnpm add @xyflow/react

Basic Setup

基础配置

typescript
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;
typescript
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;

Core Concepts

核心概念

Nodes

节点

Nodes are the building blocks of the graph. Each node has:
  • id
    : Unique identifier
  • type
    : Node type (built-in or custom)
  • position
    : { x, y } coordinates
  • data
    : Custom data object
typescript
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',
};
Built-in node types:
  • default
    : Standard node
  • input
    : No target handles
  • output
    : No source handles
  • group
    : Container for other nodes
节点是图形的构建块。每个节点包含:
  • id
    : 唯一标识符
  • type
    : 节点类型(内置或自定义)
  • position
    : { x, y } 坐标
  • data
    : 自定义数据对象
typescript
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
    : 无目标Handle
  • output
    : 无源Handle
  • group
    : 其他节点的容器

Edges

Edges connect nodes. Each edge requires:
  • id
    : Unique identifier
  • source
    : Source node ID
  • target
    : Target node ID
typescript
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 },
};
Built-in edge types:
  • default
    : Bezier curve
  • straight
    : Straight line
  • step
    : Orthogonal with sharp corners
  • smoothstep
    : Orthogonal with rounded corners
边用于连接节点。每条边需要:
  • id
    : 唯一标识符
  • source
    : 源节点ID
  • target
    : 目标节点ID
typescript
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
    : 带圆角的正交线

Handles

Handle(连接点)

Handles are connection points on nodes. Use
Position
enum for placement:
typescript
import { Handle, Position } from '@xyflow/react';

<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />
Available positions:
Position.Top
,
Position.Right
,
Position.Bottom
,
Position.Left
Handle是节点上的连接点。使用
Position
枚举来设置位置:
typescript
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

State Management

状态管理

Controlled Flow

受控流

Use state hooks for full control:
typescript
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}
    />
  );
}
使用状态钩子实现完全控制:
typescript
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 Hook

useReactFlow 钩子

Access the React Flow instance for programmatic control:
typescript
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>
  );
}
访问React Flow实例以进行程序化控制:
typescript
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>
  );
}

Custom Nodes

自定义节点

Define custom nodes using
NodeProps<T>
with typed data:
typescript
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>
  );
}
Register with
nodeTypes
:
typescript
const nodeTypes: NodeTypes = { custom: CustomNodeComponent };
<ReactFlow nodeTypes={nodeTypes} />
使用带类型数据的
NodeProps<T>
定义自定义节点:
typescript
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
注册:
typescript
const nodeTypes: NodeTypes = { custom: CustomNodeComponent };
<ReactFlow nodeTypes={nodeTypes} />

Key Patterns

关键模式

  • Multiple Handles: Use
    id
    prop and
    style
    for positioning
  • Dynamic Handles: Call
    useUpdateNodeInternals([nodeId])
    after adding/removing handles
  • Interactive Elements: Add
    className="nodrag"
    to prevent dragging on inputs/buttons
See Custom Nodes Reference for detailed patterns including styling, aviation map pins, and dynamic handles.
  • 多个Handle: 使用
    id
    属性和
    style
    进行定位
  • 动态Handle: 添加/删除Handle后调用
    useUpdateNodeInternals([nodeId])
  • 交互元素: 添加
    className="nodrag"
    以防止输入框/按钮被拖拽
有关详细模式(包括样式、航空地图标记和动态Handle),请参阅自定义节点参考

Custom Edges

自定义边

Define custom edges using
EdgeProps<T>
and path utilities:
typescript
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' }}
    />
  );
}
使用
EdgeProps<T>
和路径工具定义自定义边:
typescript
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' }}
    />
  );
}

Path Utilities

路径工具

  • getBezierPath()
    - Smooth curves
  • getStraightPath()
    - Straight lines
  • getSmoothStepPath()
    - Orthogonal with rounded corners
  • getSmoothStepPath({ borderRadius: 0 })
    - Orthogonal with sharp corners (step edge)
All return
[path, labelX, labelY, offsetX, offsetY]
.
  • getBezierPath()
    - 平滑曲线
  • getStraightPath()
    - 直线
  • getSmoothStepPath()
    - 带圆角的正交线
  • getSmoothStepPath({ borderRadius: 0 })
    - 带锐角的正交线(step边)
所有方法均返回
[path, labelX, labelY, offsetX, offsetY]

Interactive Labels

交互式标签

Use
EdgeLabelRenderer
for HTML-based labels with pointer events:
typescript
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>
    </>
  );
}
See Custom Edges Reference for animated edges, time labels, and SVG text patterns.
使用
EdgeLabelRenderer
创建支持指针事件的基于HTML的标签:
typescript
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文本模式,请参阅自定义边参考

Viewport Control

视口控制

Use
useReactFlow()
hook for programmatic viewport control:
typescript
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;
}
See Viewport Reference for save/restore state, controlled viewport, and coordinate transformations.
使用
useReactFlow()
钩子进行程序化视口控制:
typescript
import { useReactFlow } from '@xyflow/react';

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

  // 将所有节点适配到视口中
  const handleFitView = () => fitView({ padding: 0.2, duration: 400 });

  // 缩放控制
  const handleZoomIn = () => zoomIn({ duration: 300 });
  const handleZoomOut = () => zoomOut({ duration: 300 });

  // 居中到指定坐标
  const handleCenter = () => setCenter(250, 250, { zoom: 1.5, duration: 500 });

  // 将屏幕坐标转换为流坐标
  const addNodeAtClick = (event: React.MouseEvent) => {
    const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });
    // 使用该位置添加节点
  };

  return null;
}
有关保存/恢复状态、受控视口和坐标转换,请参阅视口参考

Events

事件

React Flow provides comprehensive event handling:
React Flow提供全面的事件处理:

Node Events

节点事件

typescript
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}
/>
typescript
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}
/>

Edge and Connection Events

边和连接事件

typescript
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} />
typescript
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} />

Selection and Viewport Events

选择和视口事件

typescript
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),
});
See Events Reference for complete event catalog including validation, deletion, and error handling.
typescript
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),
});
有关完整的事件目录(包括验证、删除和错误处理),请参阅事件参考

Common Patterns

常见模式

Preventing Drag/Pan

阻止拖拽/平移

typescript
<input className="nodrag" />
<button className="nodrag nopan">Click me</button>
typescript
<input className="nodrag" />
<button className="nodrag nopan">Click me</button>

Connection Validation

连接验证

typescript
const isValidConnection = (connection: Connection) => {
  return connection.source !== connection.target; // Prevent self-connections
};

<ReactFlow isValidConnection={isValidConnection} />
typescript
const isValidConnection = (connection: Connection) => {
  return connection.source !== connection.target; // 防止自连接
};

<ReactFlow isValidConnection={isValidConnection} />

Adding Nodes on Click

点击添加节点

typescript
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' } }]);
};
typescript
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' } }]);
};

Updating Node Data

更新节点数据

typescript
const { updateNodeData } = useReactFlow();
updateNodeData('node-1', { label: 'Updated' });
updateNodeData('node-1', (node) => ({ ...node.data, count: node.data.count + 1 }));
typescript
const { updateNodeData } = useReactFlow();
updateNodeData('node-1', { label: 'Updated' });
updateNodeData('node-1', (node) => ({ ...node.data, count: node.data.count + 1 }));

Provider Pattern

Provider模式

Wrap the app with
ReactFlowProvider
when using
useReactFlow()
outside the flow:
typescript
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>
  );
}
当在流外部使用
useReactFlow()
时,用
ReactFlowProvider
包裹应用:
typescript
import { ReactFlow, ReactFlowProvider, useReactFlow } from '@xyflow/react';

function Controls() {
  const { fitView } = useReactFlow(); // 必须在Provider内部
  return <button onClick={() => fitView()}>Fit View</button>;
}

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

Reference Files

参考文档

For detailed implementation patterns, see:
  • Custom Nodes - NodeProps typing, Handle component, dynamic handles, styling patterns
  • Custom Edges - EdgeProps typing, path utilities, EdgeLabelRenderer, animated edges
  • Viewport - useReactFlow methods, fitView options, coordinate conversion
  • Events - Node/edge/connection events, selection handling, viewport changes
有关详细的实现模式,请参阅:
  • 自定义节点 - NodeProps类型定义、Handle组件、动态Handle、样式模式
  • 自定义边 - EdgeProps类型定义、路径工具、EdgeLabelRenderer、动画边
  • 视口 - useReactFlow方法、fitView选项、坐标转换
  • 事件 - 节点/边/连接事件、选择处理、视口变化