Nodes
Nodes are the fundamental building blocks of your flow diagram. They represent individual units of data or functionality that can be connected together to create complex workflows.
Node Structure
Every node in Vue Flow requires two essential properties:
id (string): A unique identifier for the node
position (XYPosition): The x and y coordinates on the canvas
Basic Node
Full Node Definition
const node = {
id: '1' ,
position: { x: 100 , y: 100 },
data: { label: 'My Node' }
}
Node Interface
The complete Node interface includes:
Unique identifier for the node
Node position with x and y coordinates
Custom data object passed to your node component
type
string
default: "'default'"
Node type: 'default', 'input', 'output', or a custom type
label
string | VNode | Component
Deprecated: Node label (use data.label instead)
Position for source handles: Position.Top, Position.Right, Position.Bottom, or Position.Left
Position for target handles
Enable or disable node dragging
Enable or disable node selection
connectable
boolean | number | 'single' | HandleConnectableFunc
default: "true"
Control handle connection behavior. Use true for unlimited connections, a number for max connections, 'single' for one connection, or a function for custom logic
Enable or disable node deletion
Enable or disable keyboard focus (a11y)
Fixed width (e.g., 300 or '10rem')
Parent node ID for nested nodes (will be renamed to parentId in next major version)
extent
CoordinateExtent | 'parent'
Constrain node movement within bounds. Use 'parent' to limit to parent node boundaries
Automatically expand parent node to fit child nodes
CSS selector for drag handle element
Z-index for stacking order
class
string | string[] | ClassFunc
CSS classes or function returning classes
style
CSSProperties | StyleFunc
Inline styles or function returning styles
Built-in Node Types
Vue Flow includes three predefined node types:
A standard node with both source and target handles. const node = {
id: '1' ,
type: 'default' , // optional, this is the fallback
position: { x: 100 , y: 100 },
data: { label: 'Default Node' }
}
A starting node with only a source handle. const node = {
id: '1' ,
type: 'input' ,
position: { x: 100 , y: 100 },
data: { label: 'Input Node' },
sourcePosition: Position . Bottom
}
An ending node with only a target handle. const node = {
id: '1' ,
type: 'output' ,
position: { x: 100 , y: 100 },
data: { label: 'Output Node' },
targetPosition: Position . Top
}
Custom Node Types
Create custom nodes by defining a Vue component and registering it with Vue Flow.
CustomNode.vue
App.vue (Template Slot)
App.vue (Node Types Object)
< script setup lang = "ts" >
import { Handle , Position } from '@vue-flow/core'
import type { NodeProps } from '@vue-flow/core'
interface CustomData {
label : string
value : number
}
const props = defineProps < NodeProps < CustomData >>()
</ script >
< template >
< div class = "custom-node" >
< Handle type = "target" : position = " Position . Left " />
< div class = "content" >
< h3 > {{ props . data . label }} </ h3 >
< p > Value: {{ props . data . value }} </ p >
</ div >
< Handle type = "source" : position = " Position . Right " />
</ div >
</ template >
< style scoped >
.custom-node {
padding : 10 px ;
border-radius : 5 px ;
background : white ;
border : 2 px solid #1a192b ;
}
</ style >
When using the nodeTypes prop, remember to wrap components with markRaw() to prevent Vue from making them reactive.
GraphNode vs Node
Node : The input type you provide to Vue Flow
GraphNode : The internal representation with computed properties
interface GraphNode extends Node {
computedPosition : XYZPosition // Absolute position with z-index
handleBounds : NodeHandleBounds // Handle positions and dimensions
dimensions : Dimensions // width and height
isParent : boolean // Has child nodes
selected : boolean // Selection state
resizing : boolean // Currently being resized
dragging : boolean // Currently being dragged
}
Node Props
Your custom node components receive these props:
interface NodeProps < Data = any > {
id : string
type : string
selected : boolean
connectable : HandleConnectable
position : XYPosition
dimensions : Dimensions
data : Data
dragging : boolean
resizing : boolean
zIndex : number
targetPosition ?: Position
sourcePosition ?: Position
dragHandle ?: string
parentNodeId ?: string
}
Adding Nodes
Using v-model
Using addNodes
< script setup >
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'
const nodes = ref ([{
id: '1' ,
position: { x: 100 , y: 100 },
data: { label: 'Node 1' }
}])
function addNode () {
nodes . value . push ({
id: String ( nodes . value . length + 1 ),
position: { x: 200 , y: 200 },
data: { label: `Node ${ nodes . value . length + 1 } ` }
})
}
</ script >
< template >
< VueFlow : nodes = " nodes " />
</ template >
< script setup >
import { useVueFlow } from '@vue-flow/core'
const { addNodes } = useVueFlow ()
function addNode () {
addNodes ({
id: Date . now (). toString (),
position: { x: 200 , y: 200 },
data: { label: 'New Node' }
})
}
// Add multiple nodes
function addMultiple () {
addNodes ([
{ id: '2' , position: { x: 100 , y: 100 }, data: {} },
{ id: '3' , position: { x: 200 , y: 100 }, data: {} }
])
}
</ script >
Updating Nodes
useVueFlow
useNode (in custom node)
import { useVueFlow } from '@vue-flow/core'
const { updateNodeData , updateNode , findNode } = useVueFlow ()
// Update node data
updateNodeData ( '1' , { label: 'Updated' })
// Update using a function
updateNodeData ( '1' , ( node ) => ({ value: node . data . value + 1 }))
// Update entire node
updateNode ( '1' , { draggable: false , selectable: false })
// Find and mutate directly
const node = findNode ( '1' )
if ( node ) {
node . data . label = 'Changed'
node . position . x += 50
}
Removing Nodes
import { useVueFlow } from '@vue-flow/core'
const { removeNodes } = useVueFlow ()
// Remove single node
removeNodes ( '1' )
// Remove multiple nodes
removeNodes ([ '1' , '2' , '3' ])
// Remove with connected edges (default: true)
removeNodes ( '1' , true )
// Remove children as well
removeNodes ( '1' , true , true )
Parent-Child Relationships
Create nested nodes by setting the parentNode property:
const nodes = [
{
id: 'parent' ,
type: 'default' ,
position: { x: 0 , y: 0 },
data: { label: 'Parent' },
style: {
width: '300px' ,
height: '200px' ,
backgroundColor: 'rgba(255, 0, 0, 0.1)'
}
},
{
id: 'child' ,
type: 'default' ,
position: { x: 50 , y: 50 }, // Position relative to parent
data: { label: 'Child' },
parentNode: 'parent' ,
extent: 'parent' , // Constrain to parent boundaries
expandParent: true // Expand parent to fit child
}
]
Child node positions are relative to their parent. Use extent: 'parent' to prevent children from being dragged outside parent boundaries.
Interaction Controls
const node = {
id: '1' ,
position: { x: 0 , y: 0 },
data: {},
// Prevent dragging specific nodes
draggable: false ,
// Prevent selection
selectable: false ,
// Prevent connections
connectable: false ,
// Prevent deletion
deletable: false ,
// Use specific element as drag handle
dragHandle: '.drag-handle' ,
// Prevent user from selecting text inside node
class: 'nodrag' ,
// Prevent zoom on scroll within node
class: 'nowheel'
}
TypeScript Support
Define custom node types with type safety:
import type { Node } from '@vue-flow/core'
interface CustomData {
label : string
value : number
status : 'active' | 'inactive'
}
interface CustomEvents {
onCustomClick : ( event : MouseEvent ) => void
}
type CustomNodeType = 'custom' | 'special'
type CustomNode = Node < CustomData , CustomEvents , CustomNodeType >
const nodes : CustomNode [] = [
{
id: '1' ,
type: 'custom' , // Type-safe!
position: { x: 0 , y: 0 },
data: {
label: 'My Node' ,
value: 42 ,
status: 'active'
}
}
]
See Also