Skip to main content
Custom edges let you create unique connection styles and add interactive elements to edges. This guide shows you how to build custom edges from basic paths to complex interactive components.

Basic custom edge

Create a custom edge by defining an edge type, building a component, and registering it with Vue Flow.
1

Define your edge type

Create an edge with a custom type:
const edges = ref([
  {
    id: 'e1-2',
    source: '1',
    target: '2',
    type: 'custom',
  },
])
2

Create the edge component

Build your edge using the BezierEdge component as a base:
<script setup lang="ts">
import type { EdgeProps } from '@vue-flow/core'
import { BezierEdge } from '@vue-flow/core'

const props = defineProps<EdgeProps>()
</script>

<template>
  <BezierEdge
    :id="props.id"
    :source-x="props.sourceX"
    :source-y="props.sourceY"
    :target-x="props.targetX"
    :target-y="props.targetY"
    :source-position="props.sourcePosition"
    :target-position="props.targetPosition"
  />
</template>
3

Register the component

Register your custom edge using a template slot:
<script setup>
import { VueFlow } from '@vue-flow/core'
import CustomEdge from './CustomEdge.vue'
</script>

<template>
  <VueFlow :nodes="nodes" :edges="edges">
    <template #edge-custom="props">
      <CustomEdge v-bind="props" />
    </template>
  </VueFlow>
</template>

Edge types object

Register multiple edge types using the edgeTypes prop:
<script setup>
import { markRaw } from 'vue'
import CustomEdge from './CustomEdge.vue'
import AnimatedEdge from './AnimatedEdge.vue'

const edgeTypes = {
  custom: markRaw(CustomEdge),
  animated: markRaw(AnimatedEdge),
}
</script>

<template>
  <VueFlow :nodes="nodes" :edges="edges" :edge-types="edgeTypes" />
</template>
Wrap edge components with markRaw() to prevent Vue from making them reactive. This improves performance and prevents warnings.

Edge props

Custom edges receive position data, node references, and styling props:
<script setup lang="ts">
import type { EdgeProps } from '@vue-flow/core'

interface CustomData {
  label: string
  color: string
}

const props = defineProps<EdgeProps<CustomData>>()
</script>

Available edge props

PropTypeDescription
idstringUnique edge identifier
sourcestringSource node ID
targetstringTarget node ID
sourceNodeGraphNodeSource node object
targetNodeGraphNodeTarget node object
sourceXnumberX coordinate of source point
sourceYnumberY coordinate of source point
targetXnumberX coordinate of target point
targetYnumberY coordinate of target point
sourcePositionPositionSource handle position (Top, Right, Bottom, Left)
targetPositionPositionTarget handle position
dataobjectCustom data passed to the edge
selectedbooleanWhether edge is selected
markerStartstringStart marker ID
markerEndstringEnd marker ID

Built-in edge types

Vue Flow provides several edge types you can extend:
Smooth curved edge (default):
<script setup>
import { BezierEdge } from '@vue-flow/core'
</script>

<template>
  <BezierEdge v-bind="props" />
</template>
Rounded step edge:
<script setup>
import { SmoothStepEdge } from '@vue-flow/core'
</script>

<template>
  <SmoothStepEdge v-bind="props" />
</template>
Right-angled step edge:
<script setup>
import { StepEdge } from '@vue-flow/core'
</script>

<template>
  <StepEdge v-bind="props" />
</template>
Direct straight line:
<script setup>
import { StraightEdge } from '@vue-flow/core'
</script>

<template>
  <StraightEdge v-bind="props" />
</template>

Custom path rendering

Create a fully custom edge by rendering the SVG path directly:
<script setup lang="ts">
import type { EdgeProps, Position } from '@vue-flow/core'
import { getBezierPath } from '@vue-flow/core'

interface CustomEdgeProps extends EdgeProps {
  id: string
  sourceX: number
  sourceY: number
  targetX: number
  targetY: number
  sourcePosition: Position
  targetPosition: Position
  markerEnd: string
}

const props = defineProps<CustomEdgeProps>()

const path = computed(() => getBezierPath(props))
</script>

<template>
  <path 
    :id="id" 
    :d="path[0]" 
    :marker-end="markerEnd"
    class="vue-flow__edge-path"
    :style="{ stroke: '#ff0072', strokeWidth: 2 }"
  />
</template>

Edge labels

Add labels using the EdgeLabelRenderer:
<script setup lang="ts">
import type { EdgeProps } from '@vue-flow/core'
import { getBezierPath, EdgeLabelRenderer } from '@vue-flow/core'

const props = defineProps<EdgeProps>()

const path = computed(() => getBezierPath(props))
</script>

<template>
  <path 
    :id="id" 
    :d="path[0]" 
    :marker-end="markerEnd"
    class="vue-flow__edge-path"
  />
  
  <EdgeLabelRenderer>
    <div
      :style="{
        position: 'absolute',
        transform: `translate(-50%, -50%) translate(${path[1]}px, ${path[2]}px)`,
        pointerEvents: 'all',
      }"
      class="nodrag nopan"
    >
      <div class="edge-label">
        {{ data.label }}
      </div>
    </div>
  </EdgeLabelRenderer>
</template>

<style scoped>
.edge-label {
  background: white;
  padding: 4px 8px;
  border-radius: 4px;
  border: 1px solid #ddd;
  font-size: 12px;
}
</style>
Use EdgeLabelRenderer to position labels and buttons at the center of the edge path. The computed path returns [pathString, labelX, labelY].

Interactive edges

Add buttons and interactive elements to edges:
<script setup lang="ts">
import type { EdgeProps } from '@vue-flow/core'
import { getBezierPath, EdgeLabelRenderer, useVueFlow } from '@vue-flow/core'

const props = defineProps<EdgeProps>()
const { removeEdges } = useVueFlow()

const path = computed(() => getBezierPath(props))

function onDeleteClick() {
  removeEdges(props.id)
}
</script>

<template>
  <path 
    :id="id" 
    :d="path[0]" 
    :marker-end="markerEnd"
    class="vue-flow__edge-path"
  />
  
  <EdgeLabelRenderer>
    <div
      :style="{
        position: 'absolute',
        transform: `translate(-50%, -50%) translate(${path[1]}px, ${path[2]}px)`,
        pointerEvents: 'all',
      }"
      class="nodrag nopan"
    >
      <button class="edge-button" @click="onDeleteClick">
        ×
      </button>
    </div>
  </EdgeLabelRenderer>
</template>

<style scoped>
.edge-button {
  width: 24px;
  height: 24px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 50%;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.edge-button:hover {
  background: #f05f75;
  color: white;
  border-color: #f05f75;
}
</style>

Updating edge data

Use the useEdge composable to access and update edge data:
<script setup lang="ts">
import { useEdge } from '@vue-flow/core'

const { edge } = useEdge()

function updateEdgeData(newData: any) {
  edge.data = {
    ...edge.data,
    ...newData,
  }
}

function toggleAnimated() {
  edge.animated = !edge.animated
}
</script>

Path helpers

Vue Flow provides helper functions to generate edge paths:
import { 
  getBezierPath,
  getSmoothStepPath,
  getStraightPath,
} from '@vue-flow/core'

// Bezier curve
const [path, labelX, labelY] = getBezierPath({
  sourceX: props.sourceX,
  sourceY: props.sourceY,
  targetX: props.targetX,
  targetY: props.targetY,
  sourcePosition: props.sourcePosition,
  targetPosition: props.targetPosition,
  curvature: 0.25,  // Optional: 0-1, default is 0.25
})

// Smooth step path
const [path, labelX, labelY] = getSmoothStepPath({
  sourceX: props.sourceX,
  sourceY: props.sourceY,
  targetX: props.targetX,
  targetY: props.targetY,
  sourcePosition: props.sourcePosition,
  targetPosition: props.targetPosition,
  borderRadius: 8,  // Optional
  offset: 20,       // Optional
})

// Straight path
const [path, labelX, labelY] = getStraightPath({
  sourceX: props.sourceX,
  sourceY: props.sourceY,
  targetX: props.targetX,
  targetY: props.targetY,
})

Styling edges

Style edges using CSS or inline styles:
.vue-flow__edge-custom {
  stroke: #ff0072;
  stroke-width: 2px;
}

.vue-flow__edge-custom.selected {
  stroke: #ff6b9d;
  stroke-width: 3px;
}

.vue-flow__edge-custom .vue-flow__edge-path {
  stroke-dasharray: 5, 5;
  animation: dash 1s linear infinite;
}

@keyframes dash {
  to {
    stroke-dashoffset: -10;
  }
}
Or in edge definitions:
const edges = ref([
  {
    id: 'e1-2',
    source: '1',
    target: '2',
    type: 'custom',
    style: {
      stroke: '#ff0072',
      strokeWidth: 2,
    },
    animated: true,
  },
])

Markers

Add arrow markers to edges:
import { MarkerType } from '@vue-flow/core'

const edges = ref([
  {
    id: 'e1-2',
    source: '1',
    target: '2',
    markerEnd: {
      type: MarkerType.ArrowClosed,
      color: '#ff0072',
      width: 20,
      height: 20,
    },
    markerStart: {
      type: MarkerType.Arrow,
      color: '#0041d0',
    },
  },
])

Complete example

Here’s a complete custom edge with a delete button:
CustomEdge.vue
<script setup lang="ts">
import type { EdgeProps, Position } from '@vue-flow/core'
import { EdgeLabelRenderer, getBezierPath, useVueFlow } from '@vue-flow/core'
import type { CSSProperties } from 'vue'

interface CustomEdgeProps extends EdgeProps {
  id: string
  sourceX: number
  sourceY: number
  targetX: number
  targetY: number
  sourcePosition: Position
  targetPosition: Position
  data: any
  markerEnd: string
  style?: CSSProperties
}

const props = defineProps<CustomEdgeProps>()
const { removeEdges } = useVueFlow()

const path = computed(() => getBezierPath(props))
</script>

<script lang="ts">
export default {
  inheritAttrs: false,
}
</script>

<template>
  <path 
    :id="id" 
    :style="style" 
    class="vue-flow__edge-path" 
    :d="path[0]" 
    :marker-end="markerEnd" 
  />

  <EdgeLabelRenderer>
    <div
      :style="{
        pointerEvents: 'all',
        position: 'absolute',
        transform: `translate(-50%, -50%) translate(${path[1]}px, ${path[2]}px)`,
      }"
      class="nodrag nopan"
    >
      <button class="edge-button" @click="removeEdges(id)">×</button>
    </div>
  </EdgeLabelRenderer>
</template>

<style scoped>
.edge-button {
  border-radius: 50%;
  width: 24px;
  height: 24px;
  background: white;
  border: 1px solid #ccc;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
}

.edge-button:hover {
  box-shadow: 0 0 0 2px pink, 0 0 0 4px #f05f75;
}
</style>

Next steps

Edge events

Handle edge interactions and events

Styling guide

Advanced styling and theming

Build docs developers (and LLMs) love