React Flow Code Review
by @anderskev
Reviews React Flow code for anti-patterns, performance issues, and best practices. Use when reviewing code that uses @xyflow/react, checking for common mista...
clawhub install react-flow-code-reviewπ About This Skill
name: react-flow-code-review description: Reviews React Flow code for anti-patterns, performance issues, and best practices. Use when reviewing code that uses @xyflow/react, checking for common mistakes, or optimizing node-based UI implementations.
React Flow Code Review
When reviewing React Flow code, complete the gates below in order. Each step has an objective pass condition before moving on.
Review gates (sequenced)
1. Locate flow code β Search the review scope for ReactFlow, ReactFlowProvider, useReactFlow, @xyflow/react, nodeTypes, and edgeTypes. Pass: a short list of file paths (or explicit βnone in scopeβ after searching).
2. Provider boundary β For each useReactFlow() (and other hooks that require the provider), trace the component tree to an enclosing ReactFlowProvider, or record a concrete mismatch with file:line.
3. Stable types and memo surfaces β For each custom node or edge component, note whether it uses memo and typed props (NodeProps<...>, etc.). For each nodeTypes / edgeTypes value passed into , confirm a stable reference (module scope, or useMemo with deps you can point to) or flag unstable recreation with file:line.
4. Report with evidence β For each finding you will deliver, record file path and line number(s) (or a minimal quoted snippet). Pass: no critical or high-severity issue is stated without that citation.
5. Close the checklists β Use Performance Checklist and Common Mistakes; each item is satisfied, not applicable (with reason), or open with evidence. Pass: no item left silently ambiguous.
Critical Anti-Patterns
1. Defining nodeTypes/edgeTypes Inside Components
Problem: Causes all nodes to re-mount on every render.
// BAD - recreates object every render
function Flow() {
const nodeTypes = { custom: CustomNode }; // WRONG
return ;
}// GOOD - defined outside component
const nodeTypes = { custom: CustomNode };
function Flow() {
return ;
}
// GOOD - useMemo if dynamic
function Flow() {
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
return ;
}
2. Missing memo() on Custom Nodes/Edges
Problem: Custom components re-render on every parent update.
// BAD - no memoization
function CustomNode({ data }: NodeProps) {
return {data.label};
}// GOOD - wrapped in memo
import { memo } from 'react';
const CustomNode = memo(function CustomNode({ data }: NodeProps) {
return
{data.label};
});
3. Inline Callbacks Without useCallback
Problem: Creates new function references, breaking memoization.
// BAD - inline callback
setNodes(applyNodeChanges(changes, nodes))}
/>// GOOD - memoized callback
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
4. Using useReactFlow Outside Provider
// BAD - will throw error
function App() {
const { getNodes } = useReactFlow(); // ERROR: No provider
return ;
}// GOOD - wrap in provider
function FlowContent() {
const { getNodes } = useReactFlow(); // Works
return ;
}
function App() {
return (
);
}
5. Storing Complex Objects in Node Data
Problem: Reference equality checks fail, causing unnecessary updates.
// BAD - new object reference every time
setNodes(nodes.map(n => ({
...n,
data: { ...n.data, config: { nested: 'value' } } // New object each time
})));// GOOD - use updateNodeData for targeted updates
const { updateNodeData } = useReactFlow();
updateNodeData(nodeId, { config: { nested: 'value' } });
Performance Checklist
Node Rendering
memo()useMemouseCallbackEdge Rendering
memo()State Updates
setNodes((nds) => ...)updateNodeData for data-only changesViewport
fitView() on every renderfitViewOptions for initial fit onlyCommon Mistakes
Missing Container Height
// BAD - no height, flow won't render
// GOOD - explicit dimensions
Missing CSS Import
// Required for default styles
import '@xyflow/react/dist/style.css';
Forgetting nodrag on Interactive Elements
// BAD - clicking button drags node
// GOOD - prevents drag
Not Using Position Constants
// BAD - string literals
// GOOD - type-safe constants
import { Position } from '@xyflow/react';
Mutating Nodes/Edges Directly
// BAD - direct mutation
nodes[0].position = { x: 100, y: 100 };
setNodes(nodes);// GOOD - immutable update
setNodes(nodes.map(n =>
n.id === '1' ? { ...n, position: { x: 100, y: 100 } } : n
));
TypeScript Issues
Missing Generic Types
// BAD - loses type safety
const [nodes, setNodes] = useNodesState(initialNodes);// GOOD - explicit types
type MyNode = Node<{ value: number }, 'custom'>;
const [nodes, setNodes] = useNodesState(initialNodes);
Wrong Props Type
// BAD - using wrong type
function CustomNode(props: any) { ... }// GOOD - correct props type
function CustomNode(props: NodeProps) { ... }
Review Questions
1. Are all custom components memoized? 2. Are nodeTypes/edgeTypes defined outside render? 3. Are callbacks wrapped in useCallback? 4. Is the container sized properly? 5. Are styles imported? 6. Is useReactFlow used inside a provider? 7. Are interactive elements marked with nodrag? 8. Are types used consistently throughout?