react-flow-code-review — Claude Code skill: React Flow review
Walks @xyflow/react code through a review checklist: anti-patterns, wasted re-renders, typing. Useful when a graph stutters or nodes keep remounting.
npx skills add existential-birds/beagle --skill react-flow-code-review -a claude-codeWhat react-flow-code-review does
The skill runs the review as five sequenced gates. It first searches the review scope for ReactFlow, ReactFlowProvider, useReactFlow, @xyflow/react, nodeTypes and edgeTypes, and returns a list of file paths. Then it traces each useReactFlow() call up the component tree to an enclosing ReactFlowProvider, recording any mismatch with file and line. After that it checks whether custom nodes and edges use memo and typed props such as NodeProps<...>, and whether the nodeTypes and edgeTypes values passed to <ReactFlow> keep a stable reference.
Findings come with evidence: a file path and line numbers, or a short snippet. No critical or high-severity issue gets stated without that citation. Two checklists close the pass — performance and common mistakes — and every item ends up satisfied, not applicable with a reason, or open with evidence.
Five anti-patterns get their own sections: nodeTypes defined inside a component, missing memo, inline callbacks without useCallback, useReactFlow outside a provider, and complex objects stored in node data. Smaller traps are covered too: a container with no height, a missing @xyflow/react/dist/style.css import, a button without the nodrag class, string literals instead of Position.Right, and direct mutation of the nodes array. The skill targets projects built on @xyflow/react; point it at code that does not use the library and it has nothing to search.
What it helps with
- find why every node remounts on each render
- confirm custom nodes and edges are wrapped in
memo - catch
useReactFlowused outsideReactFlowProviderbefore it throws - stop extra updates caused by fresh object references in
data - work through the graph performance checklist and close open items
Example prompts for the agent
- review this React Flow component for anti-patterns and cite file and line
- why do my nodes remount on every render? run the react-flow-code-review skill
- go through the performance checklist in
src/Flow.tsxand flag what fails
How to install react-flow-code-review
1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a claude-code2Manually: put the files in place
~/.claude/skills/react-flow-code-review/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/react-flow-code-review/
1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a codex2Manually: put the files in place
.agents/skills/react-flow-code-review/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a cursor2Manually: put the files in place
.cursor/skills/react-flow-code-review/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install existential-birds/beagle react-flow-code-review2Manually: put the files in place
.github/skills/react-flow-code-review/SKILL.md1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a gemini-cli2Platform CLI
gemini skills install https://github.com/existential-birds/beagle --path plugins/beagle-react/skills/react-flow-code-reviewManually: put the files in place
.devin/skills/react-flow-code-review/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about react-flow-code-review
How do I install react-flow-code-review in Claude?
Run npx skills add existential-birds/beagle --skill react-flow-code-review -a claude-code. The skill lands in ~/.claude/skills/react-flow-code-review/SKILL.md.
What does react-flow-code-review do?
It reviews @xyflow/react code through five gates: locating the flow files, checking the provider boundary, verifying stable types and references, then reporting findings with file and line citations and closing the performance and common-mistake checklists.
What does the skill need to work?
The code has to use @xyflow/react — the skill searches for ReactFlow, ReactFlowProvider, useReactFlow, nodeTypes and edgeTypes. It is not built for other graph libraries.
Source text
SKILL.md · Apache-2.0
---
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 `<ReactFlow>`, 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](#performance-checklist) and [Common Mistakes](#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.
```tsx
// BAD - recreates object every render
function Flow() {
const nodeTypes = { custom: CustomNode }; // WRONG
return <ReactFlow nodeTypes={nodeTypes} />;
}
// GOOD - defined outside component
const nodeTypes = { custom: CustomNode };
function Flow() {
return <ReactFlow nodeTypes={nodeTypes} />;
}
// GOOD - useMemo if dynamic
function Flow() {
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
return <ReactFlow nodeTypes={nodeTypes} />;
}
```
### 2. Missing memo() on Custom Nodes/Edges
**Problem**: Custom components re-render on every parent update.
```tsx
// BAD - no memoization
function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
}
// GOOD - wrapped in memo
import { memo } from 'react';
const CustomNode = memo(function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
});
```
### 3. Inline Callbacks Without useCallback
**Problem**: Creates new function references, breaking memoization.
```tsx
// BAD - inline callback
<ReactFlow
onNodesChange={(changes) => setNodes(applyNodeChanges(changes, nodes))}
/>
// GOOD - memoized callback
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
<ReactFlow onNodesChange={onNodesChange} />
```
### 4. Using useReactFlow Outside Provider
```tsx
// BAD - will throw error
function App() {
const { getNodes } = useReactFlow(); // ERROR: No provider
return <ReactFlow ... />;
}
// GOOD - wrap in provider
function FlowContent() {
const { getNodes } = useReactFlow(); // Works
return <ReactFlow ... />;
}
function App() {
return (
<ReactFlowProvider>
<FlowContent />
</ReactFlowProvider>
);
}
```
### 5. Storing Complex Objects in Node Data
**Problem**: Reference equality checks fail, causing unnecessary updates.
```tsx
// 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
- [ ] Custom nodes wrapped in `memo()`
- [ ] nodeTypes defined outside component or memoized
- [ ] Heavy computations inside nodes use `useMemo`
- [ ] Event handlers use `useCallback`
### Edge Rendering
- [ ] Custom edges wrapped in `memo()`
- [ ] edgeTypes defined outside component or memoized
- [ ] Edge path calculations are not duplicated
### State Updates
- [ ] Using functional form of setState: `setNodes((nds) => ...)`
- [ ] Not spreading entire state for single property updates
- [ ] Using `updateNodeData` for data-only changes
- [ ] Batch updates when adding multiple nodes/edges
### Viewport
- [ ] Not calling `fitView()` on every render
- [ ] Using `fitViewOptions` for initial fit only
- [ ] Animation durations are reasonable (< 500ms)
## Common Mistakes
### Missing Container Height
```tsx
// BAD - no height, flow won't render
<ReactFlow nodes={nodes} edges={edges} />
// GOOD - explicit dimensions
<div style={{ width: '100%', height: '100vh' }}>
<ReactFlow nodes={nodes} edges={edges} />
</div>
```
### Missing CSS Import
```tsx
// Required for default styles
import '@xyflow/react/dist/style.css';
```
### Forgetting nodrag on Interactive Elements
```tsx
// BAD - clicking button drags node
<button onClick={handleClick}>Click</button>
// GOOD - prevents drag
<button className="nodrag" onClick={handleClick}>Click</button>
```
### Not Using Position Constants
```tsx
// BAD - string literals
<Handle type="source" position="right" />
// GOOD - type-safe constants
import { Position } from '@xyflow/react';
<Handle type="source" position={Position.Right} />
```
### Mutating Nodes/Edges Directly
```tsx
// 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
```tsx
// BAD - loses type safety
const [nodes, setNodes] = useNodesState(initialNodes);
// GOOD - explicit types
type MyNode = Node<{ value: number }, 'custom'>;
const [nodes, setNodes] = useNodesState<MyNode>(initialNodes);
```
### Wrong Props Type
```tsx
// BAD - using wrong type
function CustomNode(props: any) { ... }
// GOOD - correct props type
function CustomNode(props: NodeProps<MyNode>) { ... }
```
## 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?
© existential-birds · Apache-2.0 · existential-birds/beagle