Overview
Create interactive node-based editors, workflow builders, and visual diagrams using React Flow. Perfect for building no-code tools, data pipelines, and process automation interfaces.
Installation
pnpm add @xyflow/react
Basic Example
"use client"
import { useCallback } from "react"
import {
addEdge,
Background,
Controls,
MiniMap,
ReactFlow,
useEdgesState,
useNodesState,
} from "@xyflow/react"
import "@xyflow/react/dist/style.css"
const initialNodes = [
{ id: "1", position: { x: 0, y: 0 }, data: { label: "Start" } },
{ id: "2", position: { x: 0, y: 100 }, data: { label: "Process" } },
{ id: "3", position: { x: 0, y: 200 }, data: { label: "End" } },
]
const initialEdges = [
{ id: "e1-2", source: "1", target: "2" },
{ id: "e2-3", source: "2", target: "3" },
]
export function WorkflowBuilder() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
)
return (
<div className="h-[600px] w-full">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
>
<Controls />
<MiniMap />
<Background variant="dots" gap={12} size={1} />
</ReactFlow>
</div>
)
}
Custom Nodes
Create custom node types that match your UI design:
import { Handle, Position } from '@xyflow/react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
function CustomNode({ data }) {
return (
<Card className="w-64">
<CardHeader>
<CardTitle className="text-sm">{data.label}</CardTitle>
{data.badge && <Badge variant="outline">{data.badge}</Badge>}
</CardHeader>
<CardContent>
<p className="text-xs text-muted-foreground">{data.description}</p>
</CardContent>
<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />
</Card>
)
}
const nodeTypes = {
custom: CustomNode,
}
// Use in ReactFlow
<ReactFlow nodes={nodes} edges={edges} nodeTypes={nodeTypes} />
Use Cases
1. AI Agent Workflows
Build visual AI agent orchestration:
const aiNodes = [
{
id: "input",
type: "custom",
position: { x: 0, y: 0 },
data: { label: "User Input", badge: "Start" },
},
{
id: "llm",
type: "custom",
position: { x: 0, y: 100 },
data: { label: "LLM Agent", description: "Claude 3.5 Sonnet" },
},
{
id: "tool",
type: "custom",
position: { x: 200, y: 100 },
data: { label: "Tool Use", description: "Execute functions" },
},
{
id: "output",
type: "custom",
position: { x: 0, y: 200 },
data: { label: "Response", badge: "End" },
},
]
2. Data Pipeline Builder
Create ETL pipelines visually:
const pipelineNodes = [
{ id: "source", data: { label: "Data Source", type: "database" } },
{ id: "transform", data: { label: "Transform", type: "function" } },
{ id: "filter", data: { label: "Filter", type: "condition" } },
{ id: "sink", data: { label: "Data Sink", type: "storage" } },
]
3. UI Component Tree
Visualize component hierarchies:
const componentTree = [
{ id: "app", data: { label: "App", component: "Layout" } },
{ id: "header", data: { label: "Header", component: "Navigation" } },
{ id: "main", data: { label: "Main", component: "Content" } },
{ id: "footer", data: { label: "Footer", component: "Footer" } },
]
Styling with Hanzo UI
React Flow integrates seamlessly with Hanzo UI theming:
import { ReactFlow } from "@xyflow/react"
import "@xyflow/react/dist/style.css"
export function ThemedFlow() {
return (
<div className="h-[600px] rounded-lg border bg-background">
<ReactFlow
nodes={nodes}
edges={edges}
className="bg-background"
style={{
background: "hsl(var(--background))",
}}
>
<Controls className="border-border bg-card" />
<MiniMap className="border-border bg-card" nodeBorderRadius={8} />
<Background color="hsl(var(--muted-foreground))" gap={16} />
</ReactFlow>
</div>
)
}
Advanced Features
Edge Types
Create custom edge connections:
import { BaseEdge, EdgeLabelRenderer, getStraightPath } from "@xyflow/react"
import { Badge } from "@/components/ui/badge"
function CustomEdge({ id, sourceX, sourceY, targetX, targetY, data }) {
const [edgePath, labelX, labelY] = getStraightPath({
sourceX,
sourceY,
targetX,
targetY,
})
return (
<>
<BaseEdge path={edgePath} />
<EdgeLabelRenderer>
<div
style={{
position: "absolute",
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
>
<Badge>{data.label}</Badge>
</div>
</EdgeLabelRenderer>
</>
)
}
Interactive Controls
Add custom controls using Hanzo UI components:
import { Controls } from "@xyflow/react"
import { Maximize, ZoomIn, ZoomOut } from "lucide-react"
import { Button } from "@/components/ui/button"
;<Controls>
<Button size="icon" variant="ghost">
<ZoomIn className="h-4 w-4" />
</Button>
<Button size="icon" variant="ghost">
<ZoomOut className="h-4 w-4" />
</Button>
<Button size="icon" variant="ghost">
<Maximize className="h-4 w-4" />
</Button>
</Controls>
Persistence
Save and load workflows:
function WorkflowEditor() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
const saveWorkflow = () => {
const workflow = { nodes, edges }
localStorage.setItem("workflow", JSON.stringify(workflow))
}
const loadWorkflow = () => {
const saved = localStorage.getItem("workflow")
if (saved) {
const { nodes, edges } = JSON.parse(saved)
setNodes(nodes)
setEdges(edges)
}
}
return (
<div className="space-y-4">
<div className="flex gap-2">
<Button onClick={saveWorkflow}>Save</Button>
<Button onClick={loadWorkflow} variant="outline">
Load
</Button>
</div>
<ReactFlow nodes={nodes} edges={edges} />
</div>
)
}
Integration with Hanzo UI Components
Node Toolbar
Add actions to nodes:
import { NodeToolbar, Position } from "@xyflow/react"
import { Copy, Settings, Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button"
function NodeWithToolbar({ data }) {
return (
<>
<NodeToolbar position={Position.Top}>
<div className="flex gap-1">
<Button size="icon" variant="ghost">
<Settings className="h-4 w-4" />
</Button>
<Button size="icon" variant="ghost">
<Copy className="h-4 w-4" />
</Button>
<Button size="icon" variant="ghost">
<Trash2 className="h-4 w-4" />
</Button>
</div>
</NodeToolbar>
<Card>{/* Node content */}</Card>
</>
)
}
Context Menu
Right-click context menu:
import { useReactFlow } from "@xyflow/react"
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
function FlowWithContextMenu() {
const { addNodes, deleteElements } = useReactFlow()
return (
<ContextMenu>
<ContextMenuTrigger>
<ReactFlow nodes={nodes} edges={edges} />
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem onClick={() => addNodes(newNode)}>
Add Node
</ContextMenuItem>
<ContextMenuItem onClick={() => deleteElements({ nodes: [{ id }] })}>
Delete
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)
}
Resources
dnd-kit
React Flow
See Also
- Blocks - Browse all available blocks
- Page Builder - Build pages visually
- Components - Individual UI components