Usage with TypeScript
Vue Flow is written in TypeScript because we value the additional safety barrier it provides. We export all the types you need for correctly typing data structures and functions you pass to the Vue Flow component. We also provide a way to extend the types of nodes and edges.
Basic Usage
Let’s start with the essential types needed for a basic implementation. While TypeScript can infer some types automatically, we’ll define them explicitly for clarity.
<script setup lang="ts">
import type { DefaultEdgeOptions, Edge, FitViewOptions, Node } from '@xyflow/vue';
import { shallowRef } from 'vue';
import { Background, Controls, VueFlow } from '@xyflow/vue';
const nodes = shallowRef<Node[]>([
{
id: '1',
type: 'input',
data: { label: 'Node 1' },
position: { x: 5, y: 5 },
},
{
id: '2',
type: 'default',
data: { label: 'Node 2' },
position: { x: 5, y: 100 },
},
]);
const edges = shallowRef<Edge[]>([{ id: 'e1-2', source: '1', target: '2' }]);
const fitViewOptions: FitViewOptions = {
padding: 0.2,
};
const defaultEdgeOptions: DefaultEdgeOptions = {
animated: true,
};
</script>
<template>
<VueFlow
v-model:nodes="nodes"
v-model:edges="edges"
fit-view
:fit-view-options="fitViewOptions"
:default-edge-options="defaultEdgeOptions"
>
<Controls />
<Background variant="dots" />
</VueFlow>
</template>Custom Nodes
When working with custom nodes, you can extend the base Node type to include your custom data and pass it to NodeProps as a generic. It’s a good idea to keep these types in a shared types.ts so you can reuse them:
import type { Node } from '@xyflow/vue';
export type NumberNode = Node<{ number: number }, 'number'>;When defining node data, you must use a type alias (interfaces won’t satisfy the
Record<string, unknown> constraint on node data).
<script setup lang="ts">
import type { NodeProps } from '@xyflow/vue';
import { Handle, Position } from '@xyflow/vue';
import type { NumberNode } from './types';
const { data } = defineProps<NodeProps<NumberNode>>();
</script>
<template>
<div class="custom">
<div>A special number: {{ data.number }}</div>
<Handle type="source" :position="Position.Right" />
</div>
</template>For a single custom node that renders different content based on the node type, use a union
type and narrow on type:
<script setup lang="ts">
import type { Node, NodeProps } from '@xyflow/vue';
import { Handle, Position } from '@xyflow/vue';
type NumberNode = Node<{ number: number }, 'number'>;
type TextNode = Node<{ text: string }, 'text'>;
type AppNode = NumberNode | TextNode;
const props = defineProps<NodeProps<AppNode>>();
</script>
<template>
<div class="custom">
<div v-if="props.type === 'number'">A special number: {{ props.data.number }}</div>
<div v-else>A special text: {{ props.data.text }}</div>
<Handle type="source" :position="Position.Right" />
</div>
</template>Vue templates do not narrow props.data from a props.type check; for strict type safety,
narrow in <script setup> (for example with the type guards below).
Custom Edges
Similar to custom nodes, you can extend the base Edge type for custom edges:
<script setup lang="ts">
import type { Edge, EdgeProps } from '@xyflow/vue';
import { computed } from 'vue';
import { BaseEdge, getStraightPath } from '@xyflow/vue';
type CustomEdge = Edge<{ value: number }, 'custom'>;
const props = defineProps<EdgeProps<CustomEdge>>();
const path = computed(() =>
getStraightPath({
sourceX: props.sourceX,
sourceY: props.sourceY,
targetX: props.targetX,
targetY: props.targetY,
}),
);
</script>
<template>
<BaseEdge :id="id" :path="path[0]" />
</template>Advanced Usage
In complex applications, you’ll likely have multiple custom nodes and edges with different data structures. When using built-in functions and composables, you’ll need to properly narrow down the types to prevent runtime errors.
Node and Edge Type Unions
Many functions, callbacks, and composables (including the <VueFlow /> component) accept NodeType or EdgeType generics. These are unions of all your custom node and edge types. As long as you’ve properly typed your data objects, you can reuse their exported types, typically from a shared types.ts:
If you’re using any built-in nodes ('input', 'output', 'default', 'group'), include
the BuiltInNode type from @xyflow/vue in your node union. Built-in edges are already
covered by the base Edge type.
import type { BuiltInNode, Edge, Node } from '@xyflow/vue';
export type NumberNode = Node<{ number: number }, 'number'>;
export type TextNode = Node<{ text: string }, 'text'>;
export type CustomEdge = Edge<{ value: number }, 'custom'>;
export type AppNode = BuiltInNode | NumberNode | TextNode;
export type AppEdge = Edge | CustomEdge;You then register the components through the #node-<type> / #edge-<type> slots and bind
your typed arrays:
<script setup lang="ts">
import type { AppEdge, AppNode } from './types';
import { shallowRef } from 'vue';
import { VueFlow } from '@xyflow/vue';
import CustomEdge from './CustomEdge.vue';
import NumberNode from './NumberNode.vue';
import TextNode from './TextNode.vue';
const nodes = shallowRef<AppNode[]>([]);
const edges = shallowRef<AppEdge[]>([]);
</script>
<template>
<VueFlow v-model:nodes="nodes" v-model:edges="edges" fit-view>
<template #node-number="props">
<NumberNode v-bind="props" />
</template>
<template #node-text="props">
<TextNode v-bind="props" />
</template>
<template #edge-custom="props">
<CustomEdge v-bind="props" />
</template>
</VueFlow>
</template>Composables
You can use these type unions to properly type the return values of composables:
<script setup lang="ts">
import type { AppEdge, AppNode } from './types';
import { watchEffect } from 'vue';
import { useNodeConnections, useNodesData, useVueFlow } from '@xyflow/vue';
// nodes and edges are now correctly typed
const { getNodes, getEdges } = useVueFlow<AppNode, AppEdge>();
const connections = useNodeConnections({ handleType: 'target' });
const nodesData = useNodesData<AppNode>(() => connections.value.map(c => c.source));
watchEffect(() => {
nodesData.value.forEach((node) => {
if (node.type === 'number') {
// type-safe access to the number property
console.log(node.data.number);
}
});
});
</script>Type Guards
TypeScript provides several ways to implement type guards . One common approach is to create type guard functions like isNumberNode to filter specific nodes from a list:
import type { AppNode, NumberNode } from './types';
function isNumberNode(node: AppNode): node is NumberNode {
return node.type === 'number';
}
// numberNodes is now correctly typed as NumberNode[]
const numberNodes = computed(() => nodes.value.filter(isNumberNode));Vue Flow also ships the isNode and isEdge type guards for narrowing an element to a node
or an edge.