React Flow Architecture
v1.1.0Architectural guidance for building node-based UIs with React Flow. Use when designing flow-based applications, making decisions about state management, inte...
Security Scan
OpenClaw
Benign
high confidencePurpose & Capability
Name and description match the SKILL.md content: architecture patterns, state management, integration examples and performance guidance for React Flow. No unrelated capabilities, credentials, or system access are requested.
Instruction Scope
SKILL.md contains only explanatory text and example code/snippets (React/TypeScript). Examples reference typical integration endpoints (e.g., fetch('/api/flow')) and localStorage usage as sample application code, but do not instruct the agent to execute system commands, read system files, or exfiltrate data.
Install Mechanism
No install spec or code files are present; this is instruction-only so nothing will be written to disk or downloaded during install.
Credentials
The skill declares no required environment variables, credentials, or config paths. The examples' use of network calls is appropriate for integration guidance and does not imply any hidden credential access.
Persistence & Privilege
always is false and the skill does not request persistent privileges or modify other skills or system-wide settings. Autonomous invocation is allowed by platform default but is not combined with any broad credential or install behavior here.
Assessment
This skill is a static architecture guide and appears internally consistent with its purpose. It contains code examples that show how to integrate with an API endpoint (e.g., fetch('/api/flow')) and use localStorage — these are illustrative and won't run just by installing the skill. Before using any example in production, ensure your backend endpoints require proper authentication, avoid storing sensitive data in localStorage, and apply appropriate rate-limiting and validation. If you plan to let an agent execute code derived from these snippets, review the execution context and network permissions first.Like a lobster shell, security has layers — review code before you run it.
latest
React Flow Architecture
When to Use React Flow
Good Fit
- Visual programming interfaces
- Workflow builders and automation tools
- Diagram editors (flowcharts, org charts)
- Data pipeline visualization
- Mind mapping tools
- Node-based audio/video editors
- Decision tree builders
- State machine designers
Consider Alternatives
- Simple static diagrams (use SVG or canvas directly)
- Heavy real-time collaboration (may need custom sync layer)
- 3D visualizations (use Three.js, react-three-fiber)
- Graph analysis with 10k+ nodes (use WebGL-based solutions like Sigma.js)
Architecture Patterns
Package Structure (xyflow)
@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.
State Management Approaches
1. Local State (Simple Apps)
// 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
2. External Store (Production)
// 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
3. Redux/Other State Libraries
// Connect via selectors
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();
const onNodesChange = useCallback((changes: NodeChange[]) => {
dispatch(nodesChanged(changes));
}, [dispatch]);
Data Flow Architecture
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
Sub-Flow Pattern (Nested Nodes)
// 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:
- Use
extent: 'parent'to constrain dragging - Use
expandParent: trueto auto-expand parent - Parent z-index affects child rendering order
Viewport Persistence
// 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);
};
Integration Patterns
With Backend/API
// 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]);
With Layout Algorithms
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,
};
}
Performance Scaling
Node Count Guidelines
| Nodes | Strategy |
|---|---|
| < 100 | Default settings |
| 100-500 | Enable onlyRenderVisibleElements |
| 500-1000 | Simplify custom nodes, reduce DOM elements |
| > 1000 | Consider virtualization, WebGL alternatives |
Optimization Techniques
<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}
/>
Trade-offs
Controlled vs Uncontrolled
| Controlled | Uncontrolled |
|---|---|
| More boilerplate | Less code |
| Full state control | Internal state |
| Easy persistence | Need toObject() |
| Better for complex apps | Good for prototypes |
Connection Modes
| 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} />
Edge Rendering
| Default edges | Custom edges |
|---|---|
| Fast rendering | More control |
| Limited styling | Any SVG/HTML |
| Simple use cases | Complex labels |
Comments
Loading comments...
