Handles
Handles are the connection points on nodes that allow users to create edges by dragging from one handle to another. They are essential for building interactive flow diagrams.
Handle Component
The <Handle> component from @vue-flow/core creates connection points on your custom nodes:
< script setup >
import { Handle , Position } from '@vue-flow/core'
</ script >
< template >
< div class = "custom-node" >
< Handle
type = "target"
: position = " Position . Left "
/>
< div class = "content" >
Node Content
</ div >
< Handle
type = "source"
: position = " Position . Right "
/>
</ div >
</ template >
The <Handle> component only works inside node components. Using it outside this context will not function correctly.
Handle Types
Handles have two types that determine connection direction:
Source handles are the starting point of connections. Users drag from source handles to create new edges.
Target handles are the ending point of connections. Users drag to target handles to complete connections.
< template >
<!-- Source: connections start here -->
< Handle type = "source" : position = " Position . Right " />
<!-- Target: connections end here -->
< Handle type = "target" : position = " Position . Left " />
</ template >
Handle Positions
Handles can be placed on any side of a node using the Position enum:
import { Position } from '@vue-flow/core'
< Handle
type = "target"
: position = "Position.Left"
/>
Typically used for target handles on nodes that receive input. < Handle
type = "source"
: position = "Position.Right"
/>
Typically used for source handles on nodes that produce output. < Handle
type = "target"
: position = "Position.Top"
/>
Used for vertical flow diagrams. < Handle
type = "source"
: position = "Position.Bottom"
/>
Used for vertical flow diagrams.
Handle position affects edge curve direction. A Position.Right source creates edges that curve toward the right, while a Position.Left target creates edges that curve from the left.
Handle Props
type
'source' | 'target'
required
Determines if this handle starts or ends connections
Side of the node where handle is placed
Unique identifier when using multiple handles of the same type. Required when a node has multiple source or target handles.
connectable
boolean | number | 'single' | HandleConnectableFunc
default: "true"
Controls connection behavior:
true: Unlimited connections
false: No connections allowed
number: Maximum number of connections
'single': Only one connection
function: Custom validation logic
Whether connections can start from this handle (for source handles)
Whether connections can end at this handle (for target handles)
Function to validate if a connection attempt is allowed
Multiple Handles
Nodes can have any number of handles. When using multiple handles of the same type, each must have a unique id:
< template >
< div class = "node-with-multiple-handles" >
<!-- Multiple source handles -->
< Handle
id = "source-a"
type = "source"
: position = " Position . Right "
style = " top : 25 % "
/>
< Handle
id = "source-b"
type = "source"
: position = " Position . Right "
style = " top : 75 % "
/>
<!-- Multiple target handles -->
< Handle
id = "target-a"
type = "target"
: position = " Position . Left "
style = " top : 25 % "
/>
< Handle
id = "target-b"
type = "target"
: position = " Position . Left "
style = " top : 75 % "
/>
< div class = "content" >
Multi-Handle Node
</ div >
</ div >
</ template >
Connecting to Specific Handles
When creating edges, specify which handles to connect:
const edge = {
id: 'e1-2' ,
source: '1' ,
target: '2' ,
sourceHandle: 'source-a' , // Connect from specific source handle
targetHandle: 'target-b' // Connect to specific target handle
}
In connection events:
import { useVueFlow } from '@vue-flow/core'
const { onConnect } = useVueFlow ()
onConnect (( connection ) => {
console . log ( 'Source node:' , connection . source )
console . log ( 'Source handle:' , connection . sourceHandle ) // or null
console . log ( 'Target node:' , connection . target )
console . log ( 'Target handle:' , connection . targetHandle ) // or null
})
Handle Positioning with CSS
Handles use absolute positioning. Customize their location with CSS:
Vertical Spacing
Relative Positioning
Styled Handles
< template >
< div class = "custom-node" >
<!-- Top handle -->
< Handle
type = "target"
: position = " Position . Right "
style = " top : 10 px "
/>
<!-- Bottom handle -->
< Handle
type = "source"
: position = " Position . Right "
style = " bottom : 10 px ; top : auto ; "
/>
</ div >
</ template >
Hidden Handles
Create invisible handles for connections without visual indicators:
< template >
< Handle
type = "source"
: position = " Position . Right "
style = " opacity : 0 "
/>
</ template >
Do not use v-if or v-show to hide handles. This breaks edge calculations. Always use opacity: 0 instead.
Connection Limits
Control how many connections a handle can have:
Boolean
Number
Single
Function
<!-- Allow unlimited connections -->
< Handle type = "source" : position = " Position . Right " : connectable = " true " />
<!-- Prevent all connections -->
< Handle type = "target" : position = " Position . Left " : connectable = " false " />
<!-- Allow maximum 3 connections -->
< Handle
type = "source"
: position = " Position . Right "
: connectable = " 3 "
/>
<!-- Allow only one connection -->
< Handle
type = "source"
: position = " Position . Right "
connectable = "single"
/>
< script setup >
import type { HandleConnectableFunc } from '@vue-flow/core'
const isConnectable : HandleConnectableFunc = ( node , connectedEdges ) => {
// Custom logic: allow connections if less than 3
return connectedEdges . length < 3
}
</ script >
< template >
< Handle
type = "source"
: position = " Position . Right "
: connectable = " isConnectable "
/>
</ template >
Connection Validation
Validate connections before they’re created:
< script setup >
import type { Connection , ValidConnectionFunc } from '@vue-flow/core'
const isValidConnection : ValidConnectionFunc = (
connection : Connection ,
elements
) => {
const { sourceNode , targetNode , edges } = elements
// Prevent self-connections
if ( connection . source === connection . target ) {
return false
}
// Prevent duplicate connections
const isDuplicate = edges . some (
edge =>
edge . source === connection . source &&
edge . target === connection . target
)
if ( isDuplicate ) {
return false
}
// Custom validation logic
return sourceNode . data . type === 'output' &&
targetNode . data . type === 'input'
}
</ script >
< template >
< Handle
type = "source"
: position = " Position . Right "
: is-valid-connection = " isValidConnection "
/>
</ template >
Connection Mode
Control handle-to-handle connection rules:
< script setup >
import { ConnectionMode , VueFlow } from '@vue-flow/core'
</ script >
< template >
<!-- Loose: Allow source-to-source, target-to-target connections -->
< VueFlow : connection-mode = " ConnectionMode . Loose " />
<!-- Strict: Only allow source-to-target connections -->
< VueFlow : connection-mode = " ConnectionMode . Strict " />
</ template >
In Loose mode, Vue Flow automatically swaps source/target when connecting incompatible handles. In Strict mode, invalid connections are rejected.
Dynamic Handles
Add or update handles dynamically:
< script setup >
import { ref } from 'vue'
import { Position , Handle , useVueFlow } from '@vue-flow/core'
const { updateNodeInternals } = useVueFlow ()
const handleCount = ref ( 2 )
function addHandle () {
handleCount . value ++
// Update node internals after adding handles
updateNodeInternals ([ 'node-id' ])
}
</ script >
< template >
< div class = "dynamic-node" >
< Handle
v-for = " i in handleCount "
: key = " i "
: id = " `handle- ${ i } ` "
type = "source"
: position = " Position . Right "
: style = " { top: ` ${ ( i / ( handleCount + 1 )) * 100 } %` } "
/>
< button @ click = " addHandle " > Add Handle </ button >
</ div >
</ template >
In Vue Flow 1.x, handles auto-attach on mount. Only call updateNodeInternals if handles aren’t appearing correctly.
Handle Styling
Customize handle appearance:
< template >
< Handle
type = "source"
: position = " Position . Right "
: style = " {
width: '16px' ,
height: '16px' ,
background: '#4f46e5' ,
border: '3px solid white' ,
borderRadius: '50%'
} "
/>
</ template >
< style >
/* Global handle styles */
.vue-flow__handle {
transition : all 0.2 s ease ;
}
.vue-flow__handle:hover {
transform : scale ( 1.2 );
box-shadow : 0 0 0 4 px rgba ( 79 , 70 , 229 , 0.2 );
}
.vue-flow__handle-connecting {
background : #22c55e !important ;
}
.vue-flow__handle-valid {
background : #22c55e !important ;
}
</ style >
Handle Events
Handles don’t have direct event handlers, but you can listen to connection events:
< script setup >
import { useVueFlow } from '@vue-flow/core'
const { onConnectStart , onConnect , onConnectEnd } = useVueFlow ()
onConnectStart (( event ) => {
console . log ( 'Connection started' , event )
})
onConnect (( connection ) => {
console . log ( 'Connection created' , connection )
})
onConnectEnd (() => {
console . log ( 'Connection ended' )
})
</ script >
HandleConnectable Function
Implement complex connection logic:
import type { HandleConnectableFunc , GraphNode , GraphEdge } from '@vue-flow/core'
const handleConnectable : HandleConnectableFunc = (
node : GraphNode ,
connectedEdges : GraphEdge []
) => {
// Allow connections based on node type
if ( node . type === 'input' ) {
return connectedEdges . length < 1
}
// Check edge types
const dataEdges = connectedEdges . filter (
edge => edge . data ?. type === 'data'
)
// Allow unlimited control edges, but max 3 data edges
return dataEdges . length < 3
}
< template >
< Handle
type = "source"
: position = " Position . Right "
: connectable = " handleConnectable "
/>
</ template >
See Also
Nodes - Learn about creating custom nodes
Edges - Understand edge types and connections
State Management - Access handle connections in state