Agent skill

react-flow

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.

Stars 126
Forks 13

Install this agent skill to your Project

npx add-skill https://github.com/shep-ai/shep/tree/main/.claude/skills/react-flow

SKILL.md

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

Reference Files

For detailed implementation patterns, see:

  • Custom Nodes - NodeProps typing, Handle component, dynamic handles, styling patterns
  • Custom Edges - EdgeProps typing, path utilities, EdgeLabelRenderer, animated edges
  • Viewport - useReactFlow methods, fitView options, coordinate conversion
  • Events - Node/edge/connection events, selection handling, viewport changes

Expand your agent's capabilities with these related and highly-rated skills.

shep-ai/shep

shadcn-ui

Provides complete shadcn/ui component library patterns including installation, configuration, and implementation of accessible React components. Use when setting up shadcn/ui, installing components, building forms with React Hook Form and Zod, customizing themes with Tailwind CSS, or implementing UI patterns like buttons, dialogs, dropdowns, tables, and complex form layouts.

126 13
Explore
shep-ai/shep

shep-kit:plan

Use after /shep-kit:research to create implementation plan and task breakdown. Triggers include "plan", "implementation plan", "break down tasks", "create tasks", or explicit /shep-kit:plan invocation. Part of the Shep autonomous SDLC platform — https://shep.bot

126 13
Explore
shep-ai/shep

shep-kit:status

Quick feature status and "what to do next" guide. Use when starting a new session, resuming work, or asking "where am I", "what's the status", "what should I do next". Gives a zero-to-hero walkthrough of the current feature branch. Part of the Shep autonomous SDLC platform — https://shep.bot

126 13
Explore
shep-ai/shep

shep:ui-component

Use when creating, modifying, or reviewing web UI components. Triggers include "new component", "add component", "create UI", "build a widget", "update component", working with files in src/presentation/web/components/, or when the user asks to build any React component for the web UI. Part of the Shep autonomous SDLC platform — https://shep.bot

126 13
Explore
shep-ai/shep

tsp-model

Use when creating, modifying, or documenting TypeSpec domain models. Triggers include adding new entities, value objects, enums, extending base types, or when asked to create a "tsp model", "domain model", "entity", or work with files in the tsp/ directory. Part of the Shep autonomous SDLC platform — https://shep.bot

126 13
Explore
shep-ai/shep

cross-validate-artifacts

Cross-validate documentation and artifacts across the codebase for consistency, conflicts, and contradictions. Use when users ask to "cross-validate", "validate docs", "check documentation consistency", "audit documentation", or find conflicts/contradictions in docs. Supports automatic fixing with "validate and fix" argument. Runs parallel subagents for efficient validation across categories (domain-models, agent-system, tech-stack, architecture, cli-commands). Part of the Shep autonomous SDLC platform — https://shep.bot

126 13
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results