react-flow-advanced

Advanced React Flow patterns for complex use cases. Use when implementing sub-flows, custom connection lines, programmatic layouts, drag-and-drop, undo/redo, or complex state synchronization.

3,891 stars

Best use case

react-flow-advanced is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Advanced React Flow patterns for complex use cases. Use when implementing sub-flows, custom connection lines, programmatic layouts, drag-and-drop, undo/redo, or complex state synchronization.

Teams using react-flow-advanced should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/react-flow-advanced/SKILL.md --create-dirs "https://raw.githubusercontent.com/openclaw/skills/main/skills/anderskev/react-flow-advanced/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/react-flow-advanced/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How react-flow-advanced Compares

Feature / Agentreact-flow-advancedStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Advanced React Flow patterns for complex use cases. Use when implementing sub-flows, custom connection lines, programmatic layouts, drag-and-drop, undo/redo, or complex state synchronization.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Advanced React Flow Patterns

## Sub-Flows (Nested Nodes)

```tsx
const nodes = [
  // Parent (group) node
  {
    id: 'group-1',
    type: 'group',
    position: { x: 0, y: 0 },
    style: { width: 400, height: 300, padding: 10 },
    data: { label: 'Group' },
  },
  // Child nodes
  {
    id: 'child-1',
    parentId: 'group-1',        // Reference parent
    extent: 'parent',           // Constrain to parent bounds
    expandParent: true,         // Auto-expand parent if dragged to edge
    position: { x: 20, y: 50 }, // Relative to parent
    data: { label: 'Child 1' },
  },
  {
    id: 'child-2',
    parentId: 'group-1',
    extent: 'parent',
    position: { x: 200, y: 50 },
    data: { label: 'Child 2' },
  },
];
```

### Group Node Component

```tsx
function GroupNode({ data, id }: NodeProps) {
  return (
    <div className="group-node">
      <div className="group-header">{data.label}</div>
      {/* Children are rendered automatically by React Flow */}
    </div>
  );
}
```

## Custom Connection Line

```tsx
import { ConnectionLineComponentProps, getSmoothStepPath } from '@xyflow/react';

function CustomConnectionLine({
  fromX, fromY, fromPosition,
  toX, toY, toPosition,
  connectionStatus,
}: ConnectionLineComponentProps) {
  const [path] = getSmoothStepPath({
    sourceX: fromX,
    sourceY: fromY,
    sourcePosition: fromPosition,
    targetX: toX,
    targetY: toY,
    targetPosition: toPosition,
  });

  return (
    <g>
      <path
        d={path}
        fill="none"
        stroke={connectionStatus === 'valid' ? '#22c55e' : '#ef4444'}
        strokeWidth={2}
        strokeDasharray="5 5"
      />
    </g>
  );
}

<ReactFlow connectionLineComponent={CustomConnectionLine} />
```

## Drag and Drop from External Source

```tsx
import { useReactFlow, useCallback, useRef } from 'react';

function DnDFlow() {
  const reactFlowWrapper = useRef(null);
  const { screenToFlowPosition, addNodes } = useReactFlow();
  const [reactFlowInstance, setReactFlowInstance] = useState(null);

  const onDragOver = useCallback((event: DragEvent) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
  }, []);

  const onDrop = useCallback((event: DragEvent) => {
    event.preventDefault();

    const type = event.dataTransfer.getData('application/reactflow');
    if (!type) return;

    // Convert screen position to flow position
    const position = screenToFlowPosition({
      x: event.clientX,
      y: event.clientY,
    });

    const newNode = {
      id: `${Date.now()}`,
      type,
      position,
      data: { label: `${type} node` },
    };

    addNodes(newNode);
  }, [screenToFlowPosition, addNodes]);

  return (
    <div ref={reactFlowWrapper} style={{ height: '100%' }}>
      <ReactFlow
        onDragOver={onDragOver}
        onDrop={onDrop}
        onInit={setReactFlowInstance}
      />
    </div>
  );
}

// Sidebar component
function Sidebar() {
  const onDragStart = (event: DragEvent, nodeType: string) => {
    event.dataTransfer.setData('application/reactflow', nodeType);
    event.dataTransfer.effectAllowed = 'move';
  };

  return (
    <aside>
      <div draggable onDragStart={(e) => onDragStart(e, 'input')}>
        Input Node
      </div>
      <div draggable onDragStart={(e) => onDragStart(e, 'default')}>
        Default Node
      </div>
    </aside>
  );
}
```

## Undo/Redo

```tsx
import { useCallback, useState } from 'react';

function useUndoRedo<T>(initialState: T) {
  const [history, setHistory] = useState<T[]>([initialState]);
  const [index, setIndex] = useState(0);

  const state = history[index];

  const setState = useCallback((newState: T | ((prev: T) => T)) => {
    setHistory((prev) => {
      const resolved = typeof newState === 'function'
        ? (newState as (prev: T) => T)(prev[index])
        : newState;

      // Remove future states and add new state
      const newHistory = prev.slice(0, index + 1);
      return [...newHistory, resolved];
    });
    setIndex((i) => i + 1);
  }, [index]);

  const undo = useCallback(() => {
    setIndex((i) => Math.max(0, i - 1));
  }, []);

  const redo = useCallback(() => {
    setIndex((i) => Math.min(history.length - 1, i + 1));
  }, [history.length]);

  const canUndo = index > 0;
  const canRedo = index < history.length - 1;

  return { state, setState, undo, redo, canUndo, canRedo };
}

// Usage
function Flow() {
  const {
    state: { nodes, edges },
    setState,
    undo, redo, canUndo, canRedo
  } = useUndoRedo({ nodes: initialNodes, edges: initialEdges });

  // Capture state on significant changes
  const onNodesChange = useCallback((changes) => {
    const hasPositionChange = changes.some(c => c.type === 'position' && !c.dragging);
    if (hasPositionChange) {
      setState(prev => ({
        nodes: applyNodeChanges(changes, prev.nodes),
        edges: prev.edges,
      }));
    }
  }, [setState]);
}
```

## Programmatic Layout with dagre

```tsx
import dagre from 'dagre';

interface LayoutOptions {
  direction: 'TB' | 'BT' | 'LR' | 'RL';
  nodeWidth: number;
  nodeHeight: number;
}

function getLayoutedElements(
  nodes: Node[],
  edges: Edge[],
  options: LayoutOptions = { direction: 'TB', nodeWidth: 172, nodeHeight: 36 }
) {
  const g = new dagre.graphlib.Graph();
  g.setGraph({ rankdir: options.direction });
  g.setDefaultEdgeLabel(() => ({}));

  nodes.forEach((node) => {
    g.setNode(node.id, {
      width: node.measured?.width ?? options.nodeWidth,
      height: node.measured?.height ?? options.nodeHeight,
    });
  });

  edges.forEach((edge) => {
    g.setEdge(edge.source, edge.target);
  });

  dagre.layout(g);

  const layoutedNodes = nodes.map((node) => {
    const nodeWithPosition = g.node(node.id);
    return {
      ...node,
      position: {
        x: nodeWithPosition.x - (node.measured?.width ?? options.nodeWidth) / 2,
        y: nodeWithPosition.y - (node.measured?.height ?? options.nodeHeight) / 2,
      },
    };
  });

  return { nodes: layoutedNodes, edges };
}

// Usage after nodes are measured
function Flow() {
  const { fitView } = useReactFlow();

  const onLayout = useCallback((direction: 'TB' | 'LR') => {
    const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
      nodes,
      edges,
      { direction, nodeWidth: 150, nodeHeight: 50 }
    );

    setNodes([...layoutedNodes]);
    setEdges([...layoutedEdges]);

    window.requestAnimationFrame(() => {
      fitView({ duration: 500 });
    });
  }, [nodes, edges, setNodes, setEdges, fitView]);
}
```

## Connection with Edge on Drop

```tsx
function Flow() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const { screenToFlowPosition } = useReactFlow();

  const onConnectEnd = useCallback(
    (event: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => {
      // Only proceed if dropped on pane (not on a node)
      if (!connectionState.isValid && connectionState.fromHandle) {
        const id = `${Date.now()}`;
        const { clientX, clientY } = 'changedTouches' in event
          ? event.changedTouches[0]
          : event;

        const newNode = {
          id,
          position: screenToFlowPosition({ x: clientX, y: clientY }),
          data: { label: 'New Node' },
        };

        setNodes((nds) => [...nds, newNode]);
        setEdges((eds) => [
          ...eds,
          {
            id: `e-${connectionState.fromNode?.id}-${id}`,
            source: connectionState.fromNode?.id ?? '',
            target: id,
          },
        ]);
      }
    },
    [screenToFlowPosition, setNodes, setEdges]
  );

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnectEnd={onConnectEnd}
    />
  );
}
```

## Accessing Node Data from Edges

```tsx
import { useNodesData, type EdgeProps } from '@xyflow/react';

function DataEdge({ source, target, ...props }: EdgeProps) {
  // Get data for source and target nodes
  const nodesData = useNodesData([source, target]);
  const sourceData = nodesData[0];
  const targetData = nodesData[1];

  const [path, labelX, labelY] = getSmoothStepPath(props);

  return (
    <>
      <BaseEdge path={path} />
      <EdgeLabelRenderer>
        <div style={{ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)` }}>
          {sourceData?.data?.label} → {targetData?.data?.label}
        </div>
      </EdgeLabelRenderer>
    </>
  );
}
```

## Middleware for Node Changes

```tsx
// Filter or modify changes before they're applied
const onNodesChangeMiddleware = useCallback((changes: NodeChange[]) => {
  // Example: Prevent deletion of certain nodes
  const filteredChanges = changes.filter((change) => {
    if (change.type === 'remove') {
      const node = nodes.find((n) => n.id === change.id);
      return node?.data?.deletable !== false;
    }
    return true;
  });

  setNodes((nds) => applyNodeChanges(filteredChanges, nds));
}, [nodes, setNodes]);
```

## Keyboard Shortcuts

```tsx
import { useKeyPress } from '@xyflow/react';

function Flow() {
  const { deleteElements, getNodes, getEdges, fitView } = useReactFlow();

  // Ctrl/Cmd + A: Select all
  const selectAllPressed = useKeyPress(['Meta+a', 'Control+a']);

  useEffect(() => {
    if (selectAllPressed) {
      setNodes((nds) => nds.map((n) => ({ ...n, selected: true })));
      setEdges((eds) => eds.map((e) => ({ ...e, selected: true })));
    }
  }, [selectAllPressed]);

  // Custom delete handler
  const deletePressed = useKeyPress(['Backspace', 'Delete']);

  useEffect(() => {
    if (deletePressed) {
      const selectedNodes = getNodes().filter((n) => n.selected);
      const selectedEdges = getEdges().filter((e) => e.selected);
      deleteElements({ nodes: selectedNodes, edges: selectedEdges });
    }
  }, [deletePressed]);
}
```

## Performance: Memoizing Selectors

```tsx
import { useCallback } from 'react';
import { useStore, type ReactFlowState } from '@xyflow/react';
import { shallow } from 'zustand/shallow';

// Create stable selector outside component
const nodesSelector = (state: ReactFlowState) => state.nodes;

// Or use multiple values with shallow compare
const flowStateSelector = (state: ReactFlowState) => ({
  nodes: state.nodes,
  edges: state.edges,
  viewport: state.transform,
});

function FlowInfo() {
  const { nodes, edges, viewport } = useStore(flowStateSelector, shallow);
  return <div>Nodes: {nodes.length}, Edges: {edges.length}</div>;
}
```

Related Skills

n8n Workflow Mastery — Complete Automation Engineering System

3891
from openclaw/skills

You are an expert n8n workflow architect. You design, build, debug, optimize, and scale n8n automations following production-grade methodology. Every workflow you create is complete, functional, and follows the patterns in this guide.

Workflow & Productivity

Cash Flow Forecast

3891
from openclaw/skills

Build a 13-week rolling cash flow forecast from your actual numbers.

Finance & Investing

openclaw-safe-change-flow

3891
from openclaw/skills

Safe OpenClaw config change workflow with backup, minimal edits, validation, health checks, and rollback. Single-instance first; secondary instance optional.

DevOps & Infrastructure

n8n-workflow-automation

3891
from openclaw/skills

Designs and outputs n8n workflow JSON with robust triggers, idempotency, error handling, logging, retries, and human-in-the-loop review queues. Use when you need an auditable automation that won’t silently fail.

Workflow & Productivity

soulflow

3891
from openclaw/skills

General-purpose AI workflow framework for OpenClaw. Build custom multi-step workflows for any task — dev, ops, research, content, or automation. Ships with dev workflow examples.

Workflow & Productivity

helius-dflow

3891
from openclaw/skills

Build Solana trading applications combining DFlow trading APIs with Helius infrastructure. Covers spot swaps (imperative and declarative), prediction markets, real-time market streaming, Proof KYC, transaction submission via Sender, fee optimization, shred-level streaming via LaserStream, and wallet intelligence.

DeFi & Trading

swarm-workflow-protocol

3891
from openclaw/skills

Multi-agent orchestration protocol for the 0x-wzw swarm. Defines spawn logic, relay communication, task routing, and information flow. Agents drive decisions; humans spar.

Workflow & Productivity

agentic-workflow-automation

3891
from openclaw/skills

Generate reusable multi-step agent workflow blueprints. Use for trigger/action orchestration, deterministic workflow definitions, and automation handoff artifacts.

Workflow & Productivity

ths-advanced-analysis

3891
from openclaw/skills

基于 thsdk 进行高级股票分析:分钟K线(1m/5m/15m/30m/60m/120m)、板块/指数行情(主要指数/申万行业/概念板块成分股)、多股票批量对比(表格+归一化走势图+相关性热力图)、盘口深度、大单流向、集合竞价异动、日内分时、历史分时。当用户提到"分钟K线"、"日内走势"、"盘口"、"大单"、"竞价异动"、"板块行情"、"行业排名"、"概念板块"、"成分股"、"对比多只股票"、"批量分析"、"涨幅对比"、"相关性"、"港股"、"美股"、"外汇"、"期货"、"资讯"、"快讯",或者需要同时查看2只以上股票、关注短线交易、量化研究时,必须使用此skill。

React Next.js 项目生成器技能

3891
from openclaw/skills

## 描述

workflow-agent

3891
from openclaw/skills

选择并改写 ComfyUI 工作流模板,输出可直接提交到 ComfyUI API 的完整 JSON。当需要准备渲染任务、选择模型、调整参数时触发。

byt-workflow

3891
from openclaw/skills

YouTube video translation workflow, download audio, launch Doubao, play audio, capture translation