Skip to main content

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
const node = {
  id: '1',
  position: { x: 100, y: 100 },
  data: { label: 'My Node' }
}

Node Interface

The complete Node interface includes:
id
string
required
Unique identifier for the node
position
XYPosition
required
Node position with x and y coordinates
data
any
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)
sourcePosition
Position
Position for source handles: Position.Top, Position.Right, Position.Bottom, or Position.Left
targetPosition
Position
Position for target handles
draggable
boolean
default:"true"
Enable or disable node dragging
selectable
boolean
default:"true"
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
deletable
boolean
default:"true"
Enable or disable node deletion
focusable
boolean
default:"true"
Enable or disable keyboard focus (a11y)
width
number | string
Fixed width (e.g., 300 or '10rem')
height
number | string
Fixed height
parentNode
string
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
expandParent
boolean
Automatically expand parent node to fit child nodes
dragHandle
string
CSS selector for drag handle element
hidden
boolean
Hide the node
zIndex
number
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' }
}

Custom Node Types

Create custom nodes by defining a Vue component and registering it with Vue Flow.
<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: 10px;
  border-radius: 5px;
  background: white;
  border: 2px 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

<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>

Updating Nodes

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

Build docs developers (and LLMs) love