Connection Configuration
Options that control how connections are created, validated, and what advanced connection modes are available.
Drag from a source handle to connect — the snap radius highlights valid targets as you approach:
<div x-data="flowCanvas({
nodes: [
{ id: 'a', position: { x: 0, y: 0 }, data: { label: 'Source' } },
{ id: 'b', position: { x: 250, y: 0 }, data: { label: 'Target A' } },
{ id: 'c', position: { x: 250, y: 100 }, data: { label: 'Target B' } },
],
edges: [],
connectionSnapRadius: 40,
preventCycles: true,
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 250px;">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>
Options
| Option | Type | Default | Description |
|---|---|---|---|
isValidConnection |
(connection: Connection) => boolean |
— | Custom validator. Called after built-in checks. Return false to reject. |
connectOnClick |
boolean |
true |
Click source handle, then click target handle to connect. |
connectionSnapRadius |
number |
20 |
Pixel radius for snapping to nearby handles. 0 disables. |
connectionMode |
'strict' | 'loose' |
'strict' |
'strict' = source→target only. 'loose' = any handle to any handle. |
connectionLineType |
string |
'straight' |
Temp drag line path: 'bezier', 'smoothstep', 'straight', 'step'. |
connectionLineStyle |
object |
— | Style overrides: { stroke, strokeWidth, strokeDasharray }. |
connectionLine |
(props) => SVGElement |
— | Full custom renderer for the temp connection line. |
multiConnect |
boolean |
false |
Drag from one handle to create connections from ALL selected nodes' source handles. |
easyConnect |
boolean |
false |
Connect by holding a modifier key and dragging from node body. |
easyConnectKey |
'alt' | 'meta' | 'shift' |
'alt' |
Modifier key for easy connect. |
proximityConnect |
boolean |
false |
Auto-create edges when dragging nodes near each other. |
proximityConnectDistance |
number |
150 |
Distance threshold for proximity connect. |
proximityConnectConfirm |
boolean |
false |
Show visual confirmation before proximity edge creation. |
onProximityConnect |
(detail) => boolean | void |
— | Validate or reject a proximity connection. |
preventCycles |
boolean |
false |
Reject connections that would create directed cycles. |
connectionRules |
ConnectionRules |
— | Declarative type-based connection filtering. See Connection rules below. |
delegatedHandleEvents |
boolean |
true |
Start connect/reconnect gestures from ONE delegated pointerdown listener per canvas instead of one per handle. See Handle event delegation below. |
Handle event delegation
By default the canvas installs a single pointerdown listener that starts the
connect / reconnect gesture for every handle on it. A schema graph with thousands
of handles therefore pays for one listener, not thousands — at mount and again on
every row re-stamp.
Only pointerdown is delegated. Hover (pointerenter / pointerleave),
click-to-connect, and all keyboard/a11y handlers stay per-handle, so nothing about
the handle's own behaviour changes.
Set delegatedHandleEvents: false to restore the pre-delegation per-handle
listeners. It is read once at canvas init — patching it at runtime has no effect.
flowCanvas({
delegatedHandleEvents: false, // opt out; one pointerdown listener per handle
})
Note: under delegation the source-handle gesture stops the event during the capture phase, so a
pointerdownlistener you attach to your own markup inside a source handle will not fire. Put such listeners on an element outside the handle, or opt out withdelegatedHandleEvents: false.
Connection rules
connectionRules provides declarative type-based connection filtering. It accepts a ConnectionRules object with two optional properties:
interface ConnectionRules {
/** Map of source type → allowed target types. Unlisted source types are unrestricted. */
byType?: Record<string, string[]>;
/** Function-based validator. Return false to reject. */
validate?: (connection: Connection, sourceNode: FlowNode, targetNode: FlowNode) => boolean;
}
flowCanvas({
connectionRules: {
byType: {
trigger: ['action', 'condition'], // trigger nodes can only connect to action or condition
condition: ['action'], // condition nodes can only connect to action nodes
},
validate: (conn, source, target) => {
// Custom logic — e.g., prevent connecting to self
return source.id !== target.id;
},
},
})
Rules are checked before isValidConnection. If byType is set and the source node's type is listed, only the specified target types are allowed. Types not listed in byType are unrestricted. validate runs after byType and receives the full connection, source node, and target node.
Connection mode
In strict mode (default), source handles can only connect to target handles. In loose mode, any handle can connect to any handle — source-to-source or target-to-target:
<div x-data="flowCanvas({
nodes: [
{ id: 'a', position: { x: 0, y: 0 }, data: { label: 'Node A' } },
{ id: 'b', position: { x: 250, y: 0 }, data: { label: 'Node B' } },
],
edges: [],
connectionMode: 'loose',
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 220px;">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>