Markers
Markers are SVG arrowheads rendered at edge endpoints. Two built-in types are available:
| Marker | Visual | Description |
|---|---|---|
'arrow' |
Open chevron | Unfilled polyline arrowhead. |
'arrowclosed' |
Filled triangle | Closed and filled arrowhead. |
arrow (open) and arrowclosed (filled):
<div x-data="flowCanvas({
nodes: [
{ id: 'a', position: { x: 0, y: 0 }, data: { label: 'Source' } },
{ id: 'b', position: { x: 300, y: 80 }, data: { label: 'Target' } },
],
edges: [
{ id: 'e1', source: 'a', target: 'b', markerStart: 'arrow', markerEnd: 'arrowclosed' },
],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 250px;">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>
Usage
Use the markerEnd and markerStart properties on an edge. Both accept a shorthand string or a full MarkerConfig object:
// Shorthand string
{ id: 'e1', source: 'a', target: 'b', markerEnd: 'arrowclosed' }
// Full MarkerConfig object
{
id: 'e2',
source: 'a',
target: 'b',
markerEnd: {
type: 'arrowclosed',
color: '#ef4444',
width: 15,
height: 15,
},
markerStart: 'arrow',
}
MarkerConfig
| Property | Type | Default | Description |
|---|---|---|---|
type |
'arrow' | 'arrowclosed' |
-- | Required. Marker shape. |
color |
string |
CSS --flow-edge-stroke |
Stroke and fill color. |
width |
number |
12.5 |
Marker width in stroke-width units. |
height |
number |
12.5 |
Marker height in stroke-width units. |
orient |
string |
'auto-start-reverse' |
SVG orient attribute. |
offset |
number |
auto | Explicit distance to shift the endpoint away from the handle center. When omitted, computed automatically from handle radius and marker depth. |
Custom colored markers with MarkerConfig — bidirectional edge with markers on both ends:
<div x-data="flowCanvas({
nodes: [
{ id: 'a', position: { x: 0, y: 0 }, data: { label: 'Node A' } },
{ id: 'b', position: { x: 300, y: 80 }, data: { label: 'Node B' } },
],
edges: [
{ id: 'e1', source: 'a', target: 'b',
markerStart: { type: 'arrow', color: '#DAA532', width: 20, height: 20 },
markerEnd: { type: 'arrowclosed', color: '#8B5CF6', width: 20, height: 20 },
},
],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 220px;">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>
Custom markers
Register custom marker shapes with $flow.registerMarker(type, renderer). The renderer function receives resolved defaults and must return a complete SVG <marker> element string.
Renderer parameters
| Parameter | Type | Description |
|---|---|---|
id |
string |
Unique marker ID for the SVG <defs> element |
color |
string |
Resolved color (from edge config or theme default) |
width |
number |
Marker width in stroke-width units |
height |
number |
Marker height in stroke-width units |
orient |
string |
SVG orient attribute (usually 'auto-start-reverse') |
Registration
Register via $flow at runtime or via ES import at build time:
// Runtime (inline Alpine expressions, WireFlow x-init)
$flow.registerMarker('diamond', ({ id, color, width, height, orient }) => `
<marker id="${id}" viewBox="0 0 20 20" markerWidth="${width}" markerHeight="${height}"
orient="${orient}" markerUnits="strokeWidth" refX="20" refY="10">
<polygon points="10,0 20,10 10,20 0,10" fill="${color}" />
</marker>
`);
// Build-time (ES import)
import { registerMarker } from '@getartisanflow/alpineflow';
registerMarker('diamond', ({ id, color, width, height, orient }) => `...`);
Both methods share the same global registry. Then reference the type on any edge:
{ id: 'e1', source: 'a', target: 'b', markerEnd: 'diamond' }
// With MarkerConfig for custom color/size:
{ id: 'e2', source: 'a', target: 'b', markerEnd: { type: 'diamond', color: '#14B8A6', width: 15, height: 15 } }
Custom diamond marker in teal:
<div x-data="flowCanvas({
nodes: [
{ id: 'a', position: { x: 0, y: 0 }, data: { label: 'Custom' } },
{ id: 'b', position: { x: 300, y: 80 }, data: { label: 'Marker' } },
],
edges: [],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 220px;"
x-init="
$flow.registerMarker('diamond', ({ id, color, width, height, orient }) =>
'<marker id=\'' + id + '\' viewBox=\'0 0 20 20\' markerWidth=\'' + width + '\' markerHeight=\'' + height + '\' orient=\'' + orient + '\' markerUnits=\'strokeWidth\' refX=\'20\' refY=\'10\'><polygon points=\'10,0 20,10 10,20 0,10\' fill=\'' + color + '\' /></marker>'
);
addEdges([{ id: 'e1', source: 'a', target: 'b',
markerEnd: { type: 'diamond', color: '#14B8A6', width: 15, height: 15 },
color: '#14B8A6', strokeWidth: 2 }]);
">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>
Deduplication
Markers are deduplicated in SVG <defs> -- identical configurations (same type and color) share a single <marker> element regardless of how many edges use them.
See also
- Edge Types -- all available edge path types
- Styling -- colors, stroke width, and CSS classes
- Gradients -- gradient colors along edge strokes