Skip to main content
skill CX Codex

react-flow · existential-birds — Claude Code skill: what it does, how to install

Provides workflow diagram patterns using @xyflow/react. Helps the agent build custom nodes, handle dynamic connection points, and manage the graph viewport.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add existential-birds/beagle --skill react-flow -a claude-code
Open on GitHub

What react-flow does

The skill guides your coding agent when generating graph visualisations and interactive workflows with @xyflow/react. To use the generated components, your project needs @xyflow/react installed and its stylesheet imported from @xyflow/react/dist/style.css.

It covers typed nodes and edges via NodeProps and EdgeProps, as well as standard shapes like smoothstep or step. Connection points use the Handle component. Any interactive button inside a node must receive the nodrag class; without it, clicking an input drags the entire node.

Programmatic graph manipulation relies on the useReactFlow hook for actions like fitView, zoomIn, and element deletion. For interactive connections, the instructions apply EdgeLabelRenderer with nodrag nopan styling to preserve button click events.

What it helps with

  • build custom graph nodes with typed data using NodeProps
  • create custom edges with interactive delete buttons using EdgeLabelRenderer
  • control viewport scale and focus programmatically with useReactFlow
  • configure dynamic connection ports using Handle and Position

Example prompts for the agent

  • Create a custom process node with two target handles and status indicators
  • Add a delete button to the edge between nodes using EdgeLabelRenderer
  • Build a controlled flow with useNodesState and a fitView button

How to install react-flow

1Skills CLI (Vercel)

npx skills add existential-birds/beagle --skill react-flow -a claude-code

2Manually: put the files in place

~/.claude/skills/react-flow/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/react-flow/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about react-flow

How to install react-flow in Claude Code?

Run npx skills add existential-birds/beagle --skill react-flow -a claude-code in your terminal. This adds the skill definition to Claude Code.

What does the react-flow skill do?

It provides instructions for building diagrams with @xyflow/react. The skill covers custom node and edge components, handles, and viewport helpers.

Why do interactive elements inside custom nodes need the nodrag class?

React Flow intercepts mouse events to drag nodes across the canvas. Adding className="nodrag" keeps clicks and form inputs working inside the node.

Source text

SKILL.md · Apache-2.0
---
name: react-flow
description: React Flow (@xyflow/react) for workflow visualization with custom nodes and edges. Use when building graph visualizations, creating custom workflow nodes, implementing edge labels, or controlling viewport. Triggers on ReactFlow, @xyflow/react, Handle, NodeProps, EdgeProps, useReactFlow, fitView.
---

# 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.

## Quick Start

### Installation

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

## 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

### 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

### Handles

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`

## 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}
    />
  );
}
```

### useReactFlow Hook

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>
  );
}
```

## 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} />
```

### 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](./references/custom-nodes.md) for detailed patterns including styling, aviation map pins, and dynamic handles.

## 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' }}
    />
  );
}
```

### 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]`.

### 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](./references/custom-edges.md) for animated edges, time labels, and SVG text patterns.

## 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](./references/viewport.md) for save/restore state, controlled viewport, and coordinate transformations.

## Events

React Flow provides comprehensive event handling:

### 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}
/>
```

### 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} />
```

### 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](./references/events.md) for complete event catalog including validation, deletion, and error handling.

## Common Patterns

### Preventing Drag/Pan

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

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

### Updating Node Data

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

## Provider Pattern

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>
  );
}
```

## Implementation gates

Use these **sequenced checks** before treating an integration as done (they target common footguns, not style preferences).

1. **CSS in the bundle** — Ensure `import '@xyflow/react/dist/style.css'` runs in the app (entry or layout). **Pass:** nodes and edges have expected default styling; handles are visible and interactable.
2. **Stable `nodeTypes` / `edgeTypes`** — Do not pass a fresh object literal every render; define maps outside the component or memoize with `useMemo` and correct deps. **Pass:** no remount flicker or “maximum update depth” / runaway updates when only selection or viewport changes.
3. **Provider boundary** — Components that call `useReactFlow()` must be descendants of `ReactFlowProvider`, and the flow must actually mount. **Pass:** no missing-context error at runtime; programmatic APIs (`fitView`, etc.) work where expected.

## Reference Files

For detailed implementation patterns, see:

- [Custom Nodes](./references/custom-nodes.md) - NodeProps typing, Handle component, dynamic handles, styling patterns
- [Custom Edges](./references/custom-edges.md) - EdgeProps typing, path utilities, EdgeLabelRenderer, animated edges
- [Viewport](./references/viewport.md) - useReactFlow methods, fitView options, coordinate conversion
- [Events](./references/events.md) - Node/edge/connection events, selection handling, viewport changes

© existential-birds · Apache-2.0 · existential-birds/beagle

Similar skills

Everything for Frontend and UI →

react-flow by other authors comes first.

From the same set8

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor