Skip to Content
LearnCore ConceptsAdding Interactivity

Adding Interactivity

In the previous guide we built a flow with a couple of nodes and an edge. Because we bound our nodes and edges with v-model, that flow is already interactive: you can select, drag, and delete nodes and edges out of the box.

This is the biggest difference from React Flow and Svelte Flow. When you use v-model:nodes / v-model:edges, Vue Flow manages the state for you: it applies changes, like a node being dragged or an element being deleted, directly to your arrays. We call this an uncontrolled flow.

Uncontrolled flow with v-model

Binding nodes and edges with v-model is all you need for a flow where users can move, select, and remove elements:

<script setup lang="ts"> import type { Edge, Node } from '@xyflow/vue'; import { shallowRef } from 'vue'; import { Background, Controls, VueFlow } from '@xyflow/vue'; const nodes = shallowRef<Node[]>([ { id: 'n1', position: { x: 0, y: 0 }, data: { label: 'Node 1' }, type: 'input' }, { id: 'n2', position: { x: 100, y: 100 }, data: { label: 'Node 2' } }, ]); const edges = shallowRef<Edge[]>([]); </script> <template> <VueFlow v-model:nodes="nodes" v-model:edges="edges" fit-view> <Background /> <Controls /> </VueFlow> </template>

Vue Flow replaces the whole nodes / edges array when it applies a change, so a shallowRef is enough, and much cheaper than a deep ref for large flows.

Connecting nodes

One thing is not applied automatically: creating a new edge when the user drags from one node’s handle to another. For that, listen to the @connect event and append the new edge with the addEdge utility.

Import addEdge

import { addEdge } from '@xyflow/vue';

Add a connect handler

addEdge takes the new Connection and the current edges array, and returns a new array with the edge appended:

function onConnect(connection: Connection) { edges.value = addEdge(connection, edges.value); }

Pass it to VueFlow

<VueFlow v-model:nodes="nodes" v-model:edges="edges" fit-view @connect="onConnect"> <Background /> <Controls /> </VueFlow>

Connectable flow

Try dragging from one node’s handle to the other. The @connect handler fires and the new edge is added. 🥳

Controlled flow

Sometimes you want full control over the state so you can validate, transform, or persist every change yourself rather than letting Vue Flow apply it. To do that, drop v-model and pass nodes and edges as one-way props, then handle the @nodes-change and @edges-change events yourself — each one hands you a changeset that knows how to apply itself.

For most apps v-model is the simpler choice. Reach for a controlled flow only when you need to inspect or intercept every change before it is applied.

Handle the change events

Each handler receives a changeset of changes. Call applyTo with your current array and it returns the next one:

import type { Edge, EdgeChangeset, Node, NodeChangeset } from '@xyflow/vue'; import { addEdge } from '@xyflow/vue'; function onNodesChange(changes: NodeChangeset<Node>) { nodes.value = changes.applyTo(nodes.value); } function onEdgesChange(changes: EdgeChangeset<Edge>) { edges.value = changes.applyTo(edges.value); } function onConnect(connection: Connection) { edges.value = addEdge(connection, edges.value); }

Pass them to VueFlow

<VueFlow :nodes="nodes" :edges="edges" fit-view @nodes-change="onNodesChange" @edges-change="onEdgesChange" @connect="onConnect" > <Background /> <Controls /> </VueFlow>

Controlled flow

The result looks identical, but now every change passes through your handlers before it touches the arrays.

What you get

Whichever approach you choose, an interactive flow lets your users:

  • select nodes and edges
  • drag nodes around the canvas
  • connect nodes by dragging from one handle to another
  • draw a multi-selection box by holding Shift — the default selectionKeyCode
  • add to a selection by holding Meta/Control — the default multiSelectionKeyCode
  • remove selected elements with Backspace — the default deleteKeyCode

If you want to jump straight into building your own application, we recommend checking out the Customization section.

Last updated on