← Back to Skills Marketplace
React Flow Advanced
by
Kevin Anderson
· GitHub ↗
· v1.1.1
· MIT-0
164
Downloads
0
Stars
1
Active Installs
2
Versions
Install in OpenClaw
/install react-flow-advanced
Description
Advanced React Flow patterns for complex use cases. Use when implementing sub-flows, custom connection lines, programmatic layouts, drag-and-drop, undo/redo,...
README (SKILL.md)
Advanced React Flow Patterns
Gates (check before shipping)
Use these as sequenced checks—not “I think it works.”
- Sub-flows / groups: Pass: Every
parentIdmatches an existing nodeid; the parenttypeis registered innodeTypes; child positions are relative to the parent as intended (spot-check one drag inside/outside the group). - Custom connection line: Pass: With a valid/invalid drag, stroke or
connectionStatusvisibly differs; path renders without console errors fromgetSmoothStepPath(invalid coords). - External drag-and-drop: Pass:
onDragOveralwayspreventDefault(); drop position usesscreenToFlowPosition(not rawclientX/clientYas flow coords); new node appears under the cursor on the pane. - Undo/redo: Pass: One undo returns to the prior
{ nodes, edges }; redo restores; rapid changes do not leavecanUndo/canRedoinconsistent with visible graph (exercise add → undo → redo once). - Programmatic layout (dagre): Pass: After
setNodes, node positions match intendedrankdir;fitViewruns after layout (e.g.requestAnimationFrame) so the viewport is not stale. - Connect on drop (new node): Pass: Dropping on empty pane creates a node and an edge from the source handle; dropping on a valid target does not duplicate nodes (only the invalid-drop path adds a node).
- Selectors / store: Pass: Components that
useStorewith objects useshallow(or equivalent) so unrelated store updates do not re-render every frame.
Sub-Flows (Nested Nodes)
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
function GroupNode({ data, id }: NodeProps) {
return (
\x3Cdiv className="group-node">
\x3Cdiv className="group-header">{data.label}\x3C/div>
{/* Children are rendered automatically by React Flow */}
\x3C/div>
);
}
Custom Connection Line
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 (
\x3Cg>
\x3Cpath
d={path}
fill="none"
stroke={connectionStatus === 'valid' ? '#22c55e' : '#ef4444'}
strokeWidth={2}
strokeDasharray="5 5"
/>
\x3C/g>
);
}
\x3CReactFlow connectionLineComponent={CustomConnectionLine} />
Drag and Drop from External Source
import { useCallback, useRef, useState } from 'react';
import { useReactFlow } from '@xyflow/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 (
\x3Cdiv ref={reactFlowWrapper} style={{ height: '100%' }}>
\x3CReactFlow
onDragOver={onDragOver}
onDrop={onDrop}
onInit={setReactFlowInstance}
/>
\x3C/div>
);
}
// Sidebar component
function Sidebar() {
const onDragStart = (event: DragEvent, nodeType: string) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};
return (
\x3Caside>
\x3Cdiv draggable onDragStart={(e) => onDragStart(e, 'input')}>
Input Node
\x3C/div>
\x3Cdiv draggable onDragStart={(e) => onDragStart(e, 'default')}>
Default Node
\x3C/div>
\x3C/aside>
);
}
Undo/Redo
import { useCallback, useState } from 'react';
function useUndoRedo\x3CT>(initialState: T) {
const [history, setHistory] = useState\x3CT[]>([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 \x3C 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
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
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 (
\x3CReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnectEnd={onConnectEnd}
/>
);
}
Accessing Node Data from Edges
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 (
\x3C>
\x3CBaseEdge path={path} />
\x3CEdgeLabelRenderer>
\x3Cdiv style={{ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)` }}>
{sourceData?.data?.label} → {targetData?.data?.label}
\x3C/div>
\x3C/EdgeLabelRenderer>
\x3C/>
);
}
Middleware for Node Changes
// 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
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
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 \x3Cdiv>Nodes: {nodes.length}, Edges: {edges.length}\x3C/div>;
}
Usage Guidance
This skill is a set of code examples and QA gates for React Flow and appears coherent with its description. Before using: (1) verify the import/package names in your project (the examples import from '@xyflow/react' — confirm this matches the React Flow package you use), (2) review and test the snippets locally to ensure they match your React Flow version and coding standards, and (3) because the publisher/source is unknown and no homepage is provided, treat it as community-sourced documentation rather than an official library; prefer official documentation or packages if provenance matters to you.
Capability Analysis
Type: OpenClaw Skill
Name: react-flow-advanced
Version: 1.1.1
The skill bundle provides legitimate React/TypeScript code snippets and implementation guidelines for advanced React Flow patterns, including sub-flows, drag-and-drop functionality, and programmatic layouts using the 'dagre' library. The content in SKILL.md is strictly technical and aligns with the stated purpose, showing no signs of malicious intent, data exfiltration, or unauthorized execution.
Capability Assessment
Purpose & Capability
The name and description (advanced React Flow patterns) match the SKILL.md content: sample components, hooks, and runtime checks for React Flow behavior. There are no unrelated environment variables, binaries, or credentials requested.
Instruction Scope
The instructions are code examples and checklist gates for UI/behavioral tests. They do not instruct the agent to read system files, access environment variables, call external endpoints, or transmit data. All operations shown are client-side React patterns.
Install Mechanism
No install spec or code files are present (instruction-only), so nothing is written to disk or downloaded by the skill. Note: the skill source is unknown and there is no homepage, so you cannot verify author or provenance from this package metadata alone — but that is an author/source metadata issue, not an execution risk for the skill itself.
Credentials
The skill requests no environment variables, credentials, or config paths. The examples reference React Flow APIs only; there are no disproportionate or unrelated secrets requested.
Persistence & Privilege
The skill does not request persistent installation or elevated privileges; always is false and no special platform privileges are declared. As an instruction-only skill it has no ability to modify system or other skills' configs.
How to Use
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install react-flow-advanced - After installation, invoke the skill by name or use
/react-flow-advanced - Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.1.1
**Adds a checklist of gate/test criteria for all advanced React Flow patterns.**
- Introduced a "Gates (check before shipping)" section detailing pass/fail criteria for core patterns (sub-flows, custom connection lines, drag-and-drop, undo/redo, layouts, connect-on-drop, selectors).
- No changes to code, just improved documentation with explicit validations for each advanced use case.
- Helps ensure correct implementation and prevents common mistakes in usage.
v1.1.0
react-flow-advanced 1.1.0
- New documentation with advanced usage patterns, including:
- Sub-flows (nested/group nodes)
- Custom connection line components
- External drag-and-drop node creation
- Undo/redo state management example
- Programmatic layout with Dagre integration
- Creating nodes and edges via edge drop (connection edge on drop)
Metadata
Frequently Asked Questions
What is 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,... It is an AI Agent Skill for Claude Code / OpenClaw, with 164 downloads so far.
How do I install React Flow Advanced?
Run "/install react-flow-advanced" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is React Flow Advanced free?
Yes, React Flow Advanced is completely free, licensed under MIT-0. You can download, install and use it at no cost.
Which platforms does React Flow Advanced support?
React Flow Advanced is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created React Flow Advanced?
It is built and maintained by Kevin Anderson (@anderskev); the current version is v1.1.1.
More Skills