useHandle provides event handlers for creating custom handle components. It manages the connection logic, validation, and user interactions for connecting nodes.
It’s generally recommended to use the built-in <Handle /> component instead of this composable. Only use useHandle when you need highly customized handle behavior.
Usage
<script setup>
import { useHandle } from '@vue-flow/core'
import { ref } from 'vue'
const props = defineProps({
nodeId: String,
handleId: String,
type: String, // 'source' or 'target'
})
const handleRef = ref(null)
const { handlePointerDown, handleClick } = useHandle({
nodeId: () => props.nodeId,
handleId: () => props.handleId,
type: () => props.type,
isValidConnection: (connection) => {
// Custom validation logic
return connection.source !== connection.target
}
})
</script>
<template>
<div
ref="handleRef"
class="custom-handle"
:data-handleid="handleId"
:data-nodeid="nodeId"
:data-handlepos="type === 'source' ? 'right' : 'left'"
@mousedown="handlePointerDown"
@touchstart="handlePointerDown"
@click="handleClick"
/>
</template>
Signature
function useHandle(props: UseHandleProps): {
handlePointerDown: (event: MouseTouchEvent) => void
handleClick: (event: MouseEvent) => void
}
Parameters
Configuration object for the handle
UseHandleProps
handleId
MaybeRefOrGetter<string | null>
required
The ID of the handle. Can be a ref, getter function, or static value.
nodeId
MaybeRefOrGetter<string>
required
The ID of the node this handle belongs to. Can be a ref, getter function, or static value.
type
MaybeRefOrGetter<'source' | 'target'>
required
The type of handle - either ‘source’ (outgoing connections) or ‘target’ (incoming connections).
isValidConnection
MaybeRefOrGetter<ValidConnectionFunc | null>
Custom validation function to determine if a connection is valid.(connection: Connection) => boolean
edgeUpdaterType
MaybeRefOrGetter<'source' | 'target'>
Used when updating existing edges. Specifies which end of the edge is being updated.
onEdgeUpdate
(event: MouseTouchEvent, connection: Connection) => void
Callback fired when an edge is updated by dragging one of its ends to a new handle.
onEdgeUpdateEnd
(event: MouseTouchEvent) => void
Callback fired when edge update interaction ends.
Return Value
handlePointerDown
(event: MouseTouchEvent) => void
Event handler for mousedown/touchstart events. Initiates the connection creation process.
handleClick
(event: MouseEvent) => void
Event handler for click events. Used for click-to-connect functionality (when enabled).
Examples
Basic Custom Handle
<script setup>
import { useHandle } from '@vue-flow/core'
import { computed } from 'vue'
const props = defineProps({
nodeId: { type: String, required: true },
handleId: { type: String, default: null },
type: { type: String, required: true },
position: { type: String, default: 'right' }
})
const { handlePointerDown, handleClick } = useHandle({
nodeId: () => props.nodeId,
handleId: () => props.handleId,
type: () => props.type
})
const handleClasses = computed(() => ({
'custom-handle': true,
'source-handle': props.type === 'source',
'target-handle': props.type === 'target'
}))
</script>
<template>
<div
:class="handleClasses"
:data-handleid="handleId"
:data-nodeid="nodeId"
:data-handlepos="position"
@mousedown="handlePointerDown"
@touchstart="handlePointerDown"
@click="handleClick"
>
<div class="handle-dot" />
</div>
</template>
<style scoped>
.custom-handle {
position: absolute;
width: 16px;
height: 16px;
}
.handle-dot {
width: 100%;
height: 100%;
border-radius: 50%;
background: #555;
border: 2px solid white;
}
.source-handle {
right: -8px;
}
.target-handle {
left: -8px;
}
</style>
Handle with Validation
<script setup>
import { useHandle } from '@vue-flow/core'
import { useVueFlow } from '@vue-flow/core'
const props = defineProps({
nodeId: String,
handleId: String,
type: String,
allowedNodeTypes: Array // e.g., ['processNode', 'outputNode']
})
const { findNode } = useVueFlow()
const { handlePointerDown, handleClick } = useHandle({
nodeId: () => props.nodeId,
handleId: () => props.handleId,
type: () => props.type,
isValidConnection: (connection) => {
// Prevent self-connections
if (connection.source === connection.target) {
return false
}
// Validate target node type
const targetNode = findNode(connection.target)
if (props.allowedNodeTypes && targetNode) {
return props.allowedNodeTypes.includes(targetNode.type)
}
return true
}
})
</script>
<template>
<div
class="validated-handle"
:data-handleid="handleId"
:data-nodeid="nodeId"
:data-handlepos="type === 'source' ? 'right' : 'left'"
@mousedown="handlePointerDown"
@touchstart="handlePointerDown"
@click="handleClick"
/>
</template>
Edge Updater Handle
<script setup>
import { useHandle } from '@vue-flow/core'
import { ref } from 'vue'
const props = defineProps({
edgeId: String,
nodeId: String,
handleId: String
})
const emit = defineEmits(['edge-update', 'edge-update-end'])
const { handlePointerDown, handleClick } = useHandle({
nodeId: () => props.nodeId,
handleId: () => props.handleId,
type: () => 'source',
edgeUpdaterType: () => 'source',
onEdgeUpdate: (event, connection) => {
emit('edge-update', { edgeId: props.edgeId, connection })
},
onEdgeUpdateEnd: (event) => {
emit('edge-update-end', props.edgeId)
}
})
</script>
<template>
<div
class="edge-updater-handle"
:data-handleid="handleId"
:data-nodeid="nodeId"
@mousedown="handlePointerDown"
@touchstart="handlePointerDown"
@click="handleClick"
>
<svg width="10" height="10">
<circle cx="5" cy="5" r="4" fill="#ff6b6b" />
</svg>
</div>
</template>
Multi-Handle Node
<script setup>
import { useNode } from '@vue-flow/core'
import CustomHandle from './CustomHandle.vue'
const { node } = useNode()
const inputHandles = ['input-1', 'input-2', 'input-3']
const outputHandles = ['output-1', 'output-2']
</script>
<template>
<div class="multi-handle-node">
<div class="inputs">
<CustomHandle
v-for="handleId in inputHandles"
:key="handleId"
:node-id="node.id"
:handle-id="handleId"
type="target"
position="left"
/>
</div>
<div class="node-content">
{{ node.data.label }}
</div>
<div class="outputs">
<CustomHandle
v-for="handleId in outputHandles"
:key="handleId"
:node-id="node.id"
:handle-id="handleId"
type="source"
position="right"
/>
</div>
</div>
</template>
Important Data Attributes
When creating custom handles, you must include these data attributes on the handle element:
data-handleid - The handle ID
data-nodeid - The node ID
data-handlepos - Handle position (‘top’, ‘right’, ‘bottom’, or ‘left’)
These attributes are required for Vue Flow to properly identify and connect handles.
Notes
- All parameters can be reactive (refs or getter functions) or static values
- The composable manages auto-panning when dragging connections near viewport edges
- Connection validation happens in real-time as the user drags
- Handles emit Vue Flow events like
connectStart, connect, and connectEnd
- Use
MaybeRefOrGetter types to support reactive values without unwrapping