All components

Workflow Canvas

Headless node-based workflow canvas with support for custom node types via nodeTypes, nodeRegistry, and iconMap. Unknown nodes fall back to UnknownFlowNode.

Default
Controlled workflow with built-in add-node controls and a right-click node picker.

Add nodes with the + control, or right-click the canvas for the node picker. Cycle prevention is enabled for Zapier-style DAG workflows.

Mini Map

Sign in to view this example's source code.

Sign in
Node inspector
App-owned dialog wired via onNodeSelect and onNodeDoubleClick - canvas stays headless.

Select a node to open the inspector, or double-click to open it directly. Dialog UI lives in the app layer.

No selection
Mini Map

Sign in to view this example's source code.

Sign in
Connection validation
Typed handles with dataType/accepts block incompatible connections at connect time.

Connect boolean output to Condition (allowed) or Image Processor (blocked). Untyped handles elsewhere remain permissive.

Mini Map

Sign in to view this example's source code.

Sign in
Graph validation
validateGraph detects missing entry nodes, unreachable steps, and required branch ports. Errors surface via onValidationChange and node badges.

Intentionally invalid graph: no trigger, unreachable delay node, condition missing the false branch. Invalid nodes show a red badge on the canvas; graph-level issues appear below.

Workflow is valid

Mini Map

Sign in to view this example's source code.

Sign in

Props

NameTypeDefaultDescription
nodesWorkflowNode[]RequiredWorkflow nodes with id, type, position, and display/configuration data
edgesWorkflowEdge[]RequiredConnections between workflow nodes
nodeTypesNodeTypesundefinedReact Flow node type map. Unregistered domain types render via the built-in UnknownFlowNode fallback.
nodeRegistryRecord<string, NodeTypeConfig>{}Metadata for palette labels, handle topology, and custom node components via useWorkflowCanvasConfig. Each entry defines `handles[]` with explicit port ids.
iconMapPartial<Record<string, LucideIcon>>{}Icon lookup for custom node components reading useWorkflowCanvasConfig
WorkflowNodeData.handlesWorkflowHandleConfig[]undefinedOptional per-node handle override. When set, replaces the registry handles for that instance (e.g. merge nodes with N inputs). Edge sourceHandle/targetHandle map to handle id.
WorkflowHandleConfig.dataTypestringundefinedOutput port type for source handles (e.g. boolean, image, json). Omit for permissive legacy behavior.
WorkflowHandleConfig.acceptsstring[]undefinedAllowed input types for target handles. Omit to accept any source. Use any or * to accept all typed sources.
isValidConnectionWorkflowIsValidConnectionundefinedOptional business-rule hook after structural and port-type checks. Receives source/target nodes and resolved handles.
preventCyclesbooleanfalseWhen true, reject connections that would create a directed cycle (DAG enforcement). Also flags cycle edges in validateGraph when enabled via validationOptions.
NodeTypeConfig.role"entry" | "normal" | "exit"undefinedGraph validation role. Entry types are used as workflow start points for reachability checks.
NodeTypeConfig.requiredSourceHandlesstring[]undefinedEach listed source handle must have at least one outgoing edge when validateGraph runs.
NodeTypeConfig.requiredTargetHandlesstring[]undefinedEach listed target handle must have at least one incoming edge when validateGraph runs.
validationOptionsValidateGraphOptionsundefinedOptions passed to validateGraph. Defaults registry, isValidConnection, and preventCycles from canvas props.
validateOnChangebooleanfalseWhen true (or when onValidationChange is set), re-run validateGraph after graph mutations.
onValidationChange(result: WorkflowGraphValidationResult) => voidundefinedCalled with nodeErrors, edgeErrors, and graphErrors after validation. Also exposes result on useWorkflowCanvasConfig().validation.
editablebooleantrueEnable dragging, connecting, selection, and deletion
onNodesChange(nodes: WorkflowNode[]) => voidundefinedCalled with normalized workflow nodes after canvas changes
onEdgesChange(edges: WorkflowEdge[]) => voidundefinedCalled with normalized workflow edges after canvas changes
onConnect(connection: Connection) => voidundefinedCalled when a new edge connection is created
onNodeSelect(node: WorkflowNode | null) => voidundefinedSelection event - wire to app state; open drawers/modals in your layer, not inside the canvas
onNodeDoubleClick(node: WorkflowNode) => voidundefinedDouble-click event - convenience hook to open an inspector or dialog without embedding UI in the canvas
onAddNode(node: WorkflowNode) => voidundefinedCalled when a node is added via the canvas API
onPaneContextMenu(position, event) => voidundefinedCalled on pane right-click with flow coordinates
renderNodePicker(args) => ReactNodeundefinedRender a node picker at flow position; receives addNode and close
onNodeDrop(event, position, type) => voidundefinedOptional drag-and-drop hook for external node sources (use WORKFLOW_NODE_DRAG_TYPE)
renderControls(api: WorkflowCanvasAPI) => ReactNodeundefinedCustom controls slot with imperative canvas API
showAddNodeInControlsbooleanfalseShow add-node menu on default zoom controls
refWorkflowCanvasHandleundefinedImperative API: addNode, createNode, getNodes, getEdges, validateGraph, fitView, zoomIn, zoomOut, screenToFlowPosition
heightstring | numberundefinedExplicit canvas height; parent should size the container when omitted
autoFitViewbooleanfalseFit viewport to nodes on mount
minimapboolean | ReactNodeundefined (built-in minimap)Built-in minimap when omitted or true; false to hide; ReactNode for a custom minimap
controlsReactNode | falseundefined (built-in zoom controls)Built-in controls when omitted; false to hide; ReactNode for custom controls
backgroundbooleantrueToggle the dot grid background