Skip to Content

Devtools and Debugging

Vue Flow can feel like a black box, but you can reveal a lot about its internal state if you know where to look. This guide covers the built-in debugging aids, then shows how to build a few small inspectors of your own.

Built-in aids

  • The debug prop. Set debug on <VueFlow> and it logs each event to the console as it fires (high-frequency ones like move, drag, and hover are skipped), so you can see which events run.

    <VueFlow v-model:nodes="nodes" v-model:edges="edges" debug />
  • The @error event. Vue Flow reports problems as VueFlowError instances through the @error event — listen to it to log them or surface them to the user.

  • Vue Devtools. Because nodes and edges are ordinary Vue components, the Vue Devtools  browser extension lets you inspect the component tree, props, and reactive state of your flow like any other Vue app.

Build your own inspectors

Vue Flow doesn’t ship a devtools package, but its store is easy to read with useVueFlow(). The example below adds three small inspectors — copy any of them into your own project, they each work independently:

Viewport logger

The simplest thing to pull from the store is the viewport. useVueFlow().viewport is a { x, y, zoom } object derived from the pane transform; render it into a <Panel>:

ViewportLogger.vue
<script setup lang="ts"> import { Panel, useVueFlow } from '@xyflow/vue'; const { viewport } = useVueFlow(); </script> <template> <Panel position="bottom-right"> x: {{ viewport.x.toFixed(1) }} · y: {{ viewport.y.toFixed(1) }} · zoom: {{ viewport.zoom.toFixed(2) }} </Panel> </template>

Change logger

Every change Vue Flow makes to your nodes flows through the onNodesChange hook. Subscribe to it to log each batch as it’s dispatched — a great way to see what Vue Flow does under the hood:

ChangeLogger.vue
<script setup lang="ts"> import { Panel, useVueFlow } from '@xyflow/vue'; import { shallowRef } from 'vue'; const { onNodesChange } = useVueFlow(); const log = shallowRef<string[]>([]); onNodesChange((changes) => { log.value = [changes.toArray().map(change => change.type).join(', '), ...log.value].slice(0, 6); }); </script> <template> <Panel position="top-right"> <div v-for="(entry, index) in log" :key="index">{{ entry }}</div> </Panel> </template>

See the NodeChange and EdgeChange types for everything a change can be.

Node inspector

To render debug info that pans and zooms along with the flow, use the #zoom-pane slot — content there lives inside the transformed viewport, so you can position it in flow coordinates. Here we read every node with useVueFlow().getNodes and render its id and type above it:

NodeInspector.vue
<script setup lang="ts"> import { useVueFlow } from '@xyflow/vue'; const { getNodes } = useVueFlow(); </script> <template> <div v-for="node in getNodes" :key="node.id" class="node-inspector" :style="{ transform: `translate(${node.position.x}px, ${node.position.y - 56}px)` }" > <div>id: {{ node.id }}</div> <div>type: {{ node.type ?? 'default' }}</div> </div> </template>

Render it through the slot on <VueFlow>:

App.vue
<template> <VueFlow v-model:nodes="nodes" v-model:edges="edges"> <template #zoom-pane> <NodeInspector /> </template> </VueFlow> </template>

The #zoom-pane slot is Vue Flow’s way to render into the viewport: its content is positioned and transformed along with the nodes and edges as the user pans and zooms.

Last updated on