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.
Define your edge type
Create an edge with a custom type: const edges = ref ([
{
id: 'e1-2' ,
source: '1' ,
target: '2' ,
type: 'custom' ,
},
])
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 >
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
Prop Type Description idstring Unique edge identifier sourcestring Source node ID targetstring Target node ID sourceNodeGraphNode Source node object targetNodeGraphNode Target node object sourceXnumber X coordinate of source point sourceYnumber Y coordinate of source point targetXnumber X coordinate of target point targetYnumber Y coordinate of target point sourcePositionPosition Source handle position (Top, Right, Bottom, Left) targetPositionPosition Target handle position dataobject Custom data passed to the edge selectedboolean Whether edge is selected markerStartstring Start marker ID markerEndstring End 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 : 4 px 8 px ;
border-radius : 4 px ;
border : 1 px solid #ddd ;
font-size : 12 px ;
}
</ 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 : 24 px ;
height : 24 px ;
background : white ;
border : 1 px solid #ddd ;
border-radius : 50 % ;
cursor : pointer ;
font-size : 16 px ;
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 : 2 px ;
}
.vue-flow__edge-custom.selected {
stroke : #ff6b9d ;
stroke-width : 3 px ;
}
.vue-flow__edge-custom .vue-flow__edge-path {
stroke-dasharray : 5 , 5 ;
animation : dash 1 s 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:
< 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 : 24 px ;
height : 24 px ;
background : white ;
border : 1 px solid #ccc ;
cursor : pointer ;
font-size : 16 px ;
line-height : 1 ;
}
.edge-button:hover {
box-shadow : 0 0 0 2 px pink , 0 0 0 4 px #f05f75 ;
}
</ style >
Next steps
Edge events Handle edge interactions and events
Styling guide Advanced styling and theming