react-flow-architecture by existential-birds/beagle
npx skills add https://github.com/existential-birds/beagle --skill react-flow-architecture@xyflow/system (vanilla TypeScript)
├── 核心算法(边路径、边界、视口)
├── xypanzoom(基于 d3 的平移/缩放)
├── xydrag, xyhandle, xyminimap, xyresizer
└── 共享类型
@xyflow/react (依赖 @xyflow/system)
├── React 组件和钩子
├── 用于状态管理的 Zustand 存储
└── 特定框架的集成
@xyflow/svelte (依赖 @xyflow/system)
└── Svelte 组件和存储
含义:核心逻辑与框架无关。在贡献代码或调试时,请检查问题是在 @xyflow/system 中还是在特定框架的包中。
// 用于原型设计的 useNodesState/useEdgesState
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
优点:简单,样板代码最少 缺点:状态隔离在组件树中
// Zustand 存储示例
import { create } from 'zustand';
interface FlowStore {
nodes: Node[];
edges: Edge[];
setNodes: (nodes: Node[]) => void;
onNodesChange: OnNodesChange;
}
const useFlowStore = create<FlowStore>((set, get) => ({
nodes: initialNodes,
edges: initialEdges,
setNodes: (nodes) => set({ nodes }),
onNodesChange: (changes) => {
set({ nodes: applyNodeChanges(changes, get().nodes) });
},
}));
// 在组件中
function Flow() {
const { nodes, edges, onNodesChange } = useFlowStore();
return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}
优点:状态可在任何地方访问,更容易持久化/同步 缺点:设置更多,需要仔细优化选择器
// 通过选择器连接
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();
const onNodesChange = useCallback((changes: NodeChange[]) => {
dispatch(nodesChanged(changes));
}, [dispatch]);
用户输入 → 变更事件 → 归约器/处理器 → 状态更新 → 重新渲染
↓
[拖拽节点] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
↓
[连接] → onConnect → addEdge → setEdges → ReactFlow
↓
[删除] → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow
// 包含子节点的父节点
const nodes = [
{
id: 'group-1',
type: 'group',
position: { x: 0, y: 0 },
style: { width: 300, height: 200 },
},
{
id: 'child-1',
parentId: 'group-1', // 关键:父引用
extent: 'parent', // 关键:限制在父节点内
position: { x: 10, y: 30 }, // 相对于父节点
data: { label: '子节点' },
},
];
注意事项:
extent: 'parent' 来限制拖拽expandParent: true 来自动展开父节点// 保存视口状态
const { toObject, setViewport } = useReactFlow();
const handleSave = () => {
const flow = toObject();
// flow.nodes, flow.edges, flow.viewport
localStorage.setItem('flow', JSON.stringify(flow));
};
const handleRestore = () => {
const flow = JSON.parse(localStorage.getItem('flow'));
setNodes(flow.nodes);
setEdges(flow.edges);
setViewport(flow.viewport);
};
// 从 API 加载
useEffect(() => {
fetch('/api/flow')
.then(r => r.json())
.then(({ nodes, edges }) => {
setNodes(nodes);
setEdges(edges);
});
}, []);
// 防抖自动保存
const debouncedSave = useMemo(
() => debounce((nodes, edges) => {
fetch('/api/flow', {
method: 'POST',
body: JSON.stringify({ nodes, edges }),
});
}, 1000),
[]
);
useEffect(() => {
debouncedSave(nodes, edges);
}, [nodes, edges]);
import dagre from 'dagre';
function getLayoutedElements(nodes: Node[], edges: Edge[]) {
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: 'TB' });
g.setDefaultEdgeLabel(() => ({}));
nodes.forEach((node) => {
g.setNode(node.id, { width: 150, height: 50 });
});
edges.forEach((edge) => {
g.setEdge(edge.source, edge.target);
});
dagre.layout(g);
return {
nodes: nodes.map((node) => {
const pos = g.node(node.id);
return { ...node, position: { x: pos.x, y: pos.y } };
}),
edges,
};
}
| 节点数 | 策略 |
|---|---|
| < 100 | 默认设置 |
| 100-500 | 启用 onlyRenderVisibleElements |
| 500-1000 | 简化自定义节点,减少 DOM 元素 |
1000 | 考虑虚拟化、WebGL 替代方案
<ReactFlow
// 仅渲染视口中的节点/边
onlyRenderVisibleElements={true}
// 减少节点边框半径(改善相交计算)
nodeExtent={[[-1000, -1000], [1000, 1000]]}
// 禁用不需要的功能
elementsSelectable={false}
panOnDrag={false}
zoomOnScroll={false}
/>
| 受控 | 非受控 |
|---|---|
| 更多样板代码 | 代码更少 |
| 完全状态控制 | 内部状态 |
| 易于持久化 | 需要 toObject() |
| 更适合复杂应用 | 适合原型 |
| 严格(默认) | 宽松 |
|---|---|
| 仅源 → 目标 | 任意句柄 → 任意句柄 |
| 可预测的行为 | 更灵活 |
| 用于数据流 | 用于图表 |
<ReactFlow connectionMode={ConnectionMode.Loose} />
| 默认边 | 自定义边 |
|---|---|
| 渲染速度快 | 更多控制 |
| 样式有限 | 任意 SVG/HTML |
| 简单用例 | 复杂标签 |
每周安装量
183
代码仓库
GitHub 星标数
40
首次出现
2026年1月20日
安全审计
安装于
opencode156
gemini-cli152
codex151
github-copilot137
cursor129
claude-code127
@xyflow/system (vanilla TypeScript)
├── Core algorithms (edge paths, bounds, viewport)
├── xypanzoom (d3-based pan/zoom)
├── xydrag, xyhandle, xyminimap, xyresizer
└── Shared types
@xyflow/react (depends on @xyflow/system)
├── React components and hooks
├── Zustand store for state management
└── Framework-specific integrations
@xyflow/svelte (depends on @xyflow/system)
└── Svelte components and stores
Implication : Core logic is framework-agnostic. When contributing or debugging, check if issue is in @xyflow/system or framework-specific package.
// useNodesState/useEdgesState for prototyping
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
Pros : Simple, minimal boilerplate Cons : State isolated to component tree
// Zustand store example
import { create } from 'zustand';
interface FlowStore {
nodes: Node[];
edges: Edge[];
setNodes: (nodes: Node[]) => void;
onNodesChange: OnNodesChange;
}
const useFlowStore = create<FlowStore>((set, get) => ({
nodes: initialNodes,
edges: initialEdges,
setNodes: (nodes) => set({ nodes }),
onNodesChange: (changes) => {
set({ nodes: applyNodeChanges(changes, get().nodes) });
},
}));
// In component
function Flow() {
const { nodes, edges, onNodesChange } = useFlowStore();
return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}
Pros : State accessible anywhere, easier persistence/sync Cons : More setup, need careful selector optimization
// Connect via selectors
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();
const onNodesChange = useCallback((changes: NodeChange[]) => {
dispatch(nodesChanged(changes));
}, [dispatch]);
User Input → Change Event → Reducer/Handler → State Update → Re-render
↓
[Drag node] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
↓
[Connect] → onConnect → addEdge → setEdges → ReactFlow
↓
[Delete] → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow
// Parent node containing child nodes
const nodes = [
{
id: 'group-1',
type: 'group',
position: { x: 0, y: 0 },
style: { width: 300, height: 200 },
},
{
id: 'child-1',
parentId: 'group-1', // Key: parent reference
extent: 'parent', // Key: constrain to parent
position: { x: 10, y: 30 }, // Relative to parent
data: { label: 'Child' },
},
];
Considerations :
extent: 'parent' to constrain draggingexpandParent: true to auto-expand parent// Save viewport state
const { toObject, setViewport } = useReactFlow();
const handleSave = () => {
const flow = toObject();
// flow.nodes, flow.edges, flow.viewport
localStorage.setItem('flow', JSON.stringify(flow));
};
const handleRestore = () => {
const flow = JSON.parse(localStorage.getItem('flow'));
setNodes(flow.nodes);
setEdges(flow.edges);
setViewport(flow.viewport);
};
// Load from API
useEffect(() => {
fetch('/api/flow')
.then(r => r.json())
.then(({ nodes, edges }) => {
setNodes(nodes);
setEdges(edges);
});
}, []);
// Debounced auto-save
const debouncedSave = useMemo(
() => debounce((nodes, edges) => {
fetch('/api/flow', {
method: 'POST',
body: JSON.stringify({ nodes, edges }),
});
}, 1000),
[]
);
useEffect(() => {
debouncedSave(nodes, edges);
}, [nodes, edges]);
import dagre from 'dagre';
function getLayoutedElements(nodes: Node[], edges: Edge[]) {
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: 'TB' });
g.setDefaultEdgeLabel(() => ({}));
nodes.forEach((node) => {
g.setNode(node.id, { width: 150, height: 50 });
});
edges.forEach((edge) => {
g.setEdge(edge.source, edge.target);
});
dagre.layout(g);
return {
nodes: nodes.map((node) => {
const pos = g.node(node.id);
return { ...node, position: { x: pos.x, y: pos.y } };
}),
edges,
};
}
| Nodes | Strategy |
|---|---|
| < 100 | Default settings |
| 100-500 | Enable onlyRenderVisibleElements |
| 500-1000 | Simplify custom nodes, reduce DOM elements |
1000 | Consider virtualization, WebGL alternatives
<ReactFlow
// Only render nodes/edges in viewport
onlyRenderVisibleElements={true}
// Reduce node border radius (improves intersect calculations)
nodeExtent={[[-1000, -1000], [1000, 1000]]}
// Disable features not needed
elementsSelectable={false}
panOnDrag={false}
zoomOnScroll={false}
/>
| Controlled | Uncontrolled |
|---|---|
| More boilerplate | Less code |
| Full state control | Internal state |
| Easy persistence | Need toObject() |
| Better for complex apps | Good for prototypes |
| Strict (default) | Loose |
|---|---|
| Source → Target only | Any handle → any handle |
| Predictable behavior | More flexible |
| Use for data flows | Use for diagrams |
<ReactFlow connectionMode={ConnectionMode.Loose} />
| Default edges | Custom edges |
|---|---|
| Fast rendering | More control |
| Limited styling | Any SVG/HTML |
| Simple use cases | Complex labels |
Weekly Installs
183
Repository
GitHub Stars
40
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode156
gemini-cli152
codex151
github-copilot137
cursor129
claude-code127
TanStack Query v5 完全指南:React 数据管理、乐观更新、离线支持
2,500 周安装
OpenAI语音生成技能:文本转语音、旁白配音、批量TTS生成,支持GPT-4o mini TTS
681 周安装
Jina Reader:AI网页内容提取工具,保护IP,支持搜索与事实核查
674 周安装
UX启发式框架:10个可用性原则评估界面,提升用户体验设计
721 周安装
交易策略回测框架:8种内置策略、性能指标与参数优化
688 周安装
OpenAI Skill Creator 指南:创建高效技能模块,扩展AI代理能力
698 周安装
AI技能开发与验证:Day4 Wrap & Analyze 完整指南 - 掌握Multi-Agent与2-Phase Pipeline
700 周安装