Use edge specifications to choose a route, draw labels and arrowheads, and make crossing edges visibly hop. A mounted diagram renders each of these settings as part of its live graph.
This page focuses on the visual changes edge settings make in a mounted graph; for framework-specific setup and entry points, see Build runnable workflows.
Set route geometry and appearance
For the distinction among type, router, and connector, see Diagram intent and rendering; this mounted graph shows the visible differences: sharp versus rounded corners, distinct labels and markers, and hops at crossings.
The style field accepts a partial LinkStyle; it carries stroke properties, arrowheads, and crossing configuration. In all the framework samples, the canvas has a fixed height so the rendered diagram has space to appear.
jsimport { render } from '@grafloria/element';
const target = document.getElementById('app');
if (!target) throw new Error('Missing #app');
target.style.height = '900px';
const nodes = [
{ id: 'a', position: { x: 80, y: 80 }, size: { width: 140, height: 56 }, label: 'Direct' },
{ id: 'b', position: { x: 570, y: 150 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'c', position: { x: 80, y: 230 }, size: { width: 140, height: 56 }, label: 'Sharp step' },
{ id: 'd', position: { x: 570, y: 300 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'e', position: { x: 80, y: 380 }, size: { width: 140, height: 56 }, label: 'Rounded step' },
{ id: 'f', position: { x: 570, y: 450 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'g', position: { x: 80, y: 530 }, size: { width: 140, height: 56 }, label: 'Bezier' },
{ id: 'h', position: { x: 570, y: 600 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'i', position: { x: 760, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross A' },
{ id: 'j', position: { x: 1040, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross D' },
{ id: 'k', position: { x: 760, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross B' },
{ id: 'l', position: { x: 1040, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross C' },
];
const edges = [
{ id: 'direct', source: 'a', target: 'b', type: 'direct', label: 'direct', labelPlacement: 'above', labelStyle: { color: '#1d4ed8', fontSize: 12 }, style: { stroke: '#2563eb', strokeWidth: 2, arrowHead: { type: 'arrow', size: 14, filled: true } } },
{ id: 'sharp', source: 'c', target: 'd', router: 'orthogonal', connector: 'straight', label: 'sharp corners', labelPlacement: 'above', style: { stroke: '#475569', strokeDasharray: '6 3', arrowHead: { type: 'diamond', size: 14, filled: false } } },
{ id: 'rounded', source: 'e', target: 'f', router: 'orthogonal', connector: 'rounded', label: 'rounded corners', labelPlacement: 'below', labelStyle: { background: '#eff6ff', padding: 4, borderRadius: 4 }, style: { stroke: '#059669', strokeWidth: 2, arrowHead: { type: 'crow-foot', size: 14, filled: false } } },
{ id: 'curve', source: 'g', target: 'h', type: 'bezier', label: 'no arrowhead', style: { stroke: '#9333ea', strokeWidth: 2, arrowHead: { type: 'none', size: 14, filled: false } } },
{ id: 'cross-ad', source: 'i', target: 'l', type: 'direct', style: { stroke: '#0f766e', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
{ id: 'cross-bc', source: 'k', target: 'j', type: 'direct', style: { stroke: '#b91c1c', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
];
render({ nodes, edges }, target);
tsimport { Component } from '@angular/core';
import { DiagramCanvasComponent } from '@grafloria/angular';
import type { EdgeSpec } from '@grafloria/renderer';
@Component({
standalone: true,
imports: [DiagramCanvasComponent],
template: `
<grafloria-diagram-canvas [(nodes)]="nodes" [(edges)]="edges"
style="display:block; height:900px" />
`,
})
export class EdgeStylesComponent {
nodes = [
{ id: 'a', position: { x: 80, y: 80 }, size: { width: 140, height: 56 }, label: 'Direct' },
{ id: 'b', position: { x: 570, y: 150 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'c', position: { x: 80, y: 230 }, size: { width: 140, height: 56 }, label: 'Sharp step' },
{ id: 'd', position: { x: 570, y: 300 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'e', position: { x: 80, y: 380 }, size: { width: 140, height: 56 }, label: 'Rounded step' },
{ id: 'f', position: { x: 570, y: 450 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'g', position: { x: 80, y: 530 }, size: { width: 140, height: 56 }, label: 'Bezier' },
{ id: 'h', position: { x: 570, y: 600 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'i', position: { x: 760, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross A' },
{ id: 'j', position: { x: 1040, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross D' },
{ id: 'k', position: { x: 760, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross B' },
{ id: 'l', position: { x: 1040, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross C' },
];
edges: EdgeSpec[] = [
{ id: 'direct', source: 'a', target: 'b', type: 'direct', label: 'direct', labelPlacement: 'above', labelStyle: { color: '#1d4ed8', fontSize: 12 }, style: { stroke: '#2563eb', strokeWidth: 2, arrowHead: { type: 'arrow', size: 14, filled: true } } },
{ id: 'sharp', source: 'c', target: 'd', router: 'orthogonal', connector: 'straight', label: 'sharp corners', labelPlacement: 'above', style: { stroke: '#475569', strokeDasharray: '6 3', arrowHead: { type: 'diamond', size: 14, filled: false } } },
{ id: 'rounded', source: 'e', target: 'f', router: 'orthogonal', connector: 'rounded', label: 'rounded corners', labelPlacement: 'below', labelStyle: { background: '#eff6ff', padding: 4, borderRadius: 4 }, style: { stroke: '#059669', strokeWidth: 2, arrowHead: { type: 'crow-foot', size: 14, filled: false } } },
{ id: 'curve', source: 'g', target: 'h', type: 'bezier', label: 'no arrowhead', style: { stroke: '#9333ea', strokeWidth: 2, arrowHead: { type: 'none', size: 14, filled: false } } },
{ id: 'cross-ad', source: 'i', target: 'l', type: 'direct', style: { stroke: '#0f766e', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
{ id: 'cross-bc', source: 'k', target: 'j', type: 'direct', style: { stroke: '#b91c1c', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
];
}
tsximport { render as renderQwik } from '@builder.io/qwik';
import { GrafloriaFlow, type EdgeSpec } from '@grafloria/qwik';
const nodes = [
{ id: 'a', position: { x: 80, y: 80 }, size: { width: 140, height: 56 }, label: 'Direct' },
{ id: 'b', position: { x: 570, y: 150 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'c', position: { x: 80, y: 230 }, size: { width: 140, height: 56 }, label: 'Sharp step' },
{ id: 'd', position: { x: 570, y: 300 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'e', position: { x: 80, y: 380 }, size: { width: 140, height: 56 }, label: 'Rounded step' },
{ id: 'f', position: { x: 570, y: 450 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'g', position: { x: 80, y: 530 }, size: { width: 140, height: 56 }, label: 'Bezier' },
{ id: 'h', position: { x: 570, y: 600 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'i', position: { x: 760, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross A' },
{ id: 'j', position: { x: 1040, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross D' },
{ id: 'k', position: { x: 760, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross B' },
{ id: 'l', position: { x: 1040, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross C' },
];
const edges: EdgeSpec[] = [
{ id: 'direct', source: 'a', target: 'b', type: 'direct', label: 'direct', labelPlacement: 'above', labelStyle: { color: '#1d4ed8', fontSize: 12 }, style: { stroke: '#2563eb', strokeWidth: 2, arrowHead: { type: 'arrow', size: 14, filled: true } } },
{ id: 'sharp', source: 'c', target: 'd', router: 'orthogonal', connector: 'straight', label: 'sharp corners', labelPlacement: 'above', style: { stroke: '#475569', strokeDasharray: '6 3', arrowHead: { type: 'diamond', size: 14, filled: false } } },
{ id: 'rounded', source: 'e', target: 'f', router: 'orthogonal', connector: 'rounded', label: 'rounded corners', labelPlacement: 'below', labelStyle: { background: '#eff6ff', padding: 4, borderRadius: 4 }, style: { stroke: '#059669', strokeWidth: 2, arrowHead: { type: 'crow-foot', size: 14, filled: false } } },
{ id: 'curve', source: 'g', target: 'h', type: 'bezier', label: 'no arrowhead', style: { stroke: '#9333ea', strokeWidth: 2, arrowHead: { type: 'none', size: 14, filled: false } } },
{ id: 'cross-ad', source: 'i', target: 'l', type: 'direct', style: { stroke: '#0f766e', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
{ id: 'cross-bc', source: 'k', target: 'j', type: 'direct', style: { stroke: '#b91c1c', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
];
const target = typeof document === 'undefined' ? null : document.createElement('div');
if (target) {
target.style.height = '900px';
document.body.append(target);
void renderQwik(target, <GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} />);
}
tsximport { GrafloriaFlow } from '@grafloria/react';
import type { EdgeSpec } from '@grafloria/renderer';
const nodes = [
{ id: 'a', position: { x: 80, y: 80 }, size: { width: 140, height: 56 }, label: 'Direct' },
{ id: 'b', position: { x: 570, y: 150 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'c', position: { x: 80, y: 230 }, size: { width: 140, height: 56 }, label: 'Sharp step' },
{ id: 'd', position: { x: 570, y: 300 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'e', position: { x: 80, y: 380 }, size: { width: 140, height: 56 }, label: 'Rounded step' },
{ id: 'f', position: { x: 570, y: 450 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'g', position: { x: 80, y: 530 }, size: { width: 140, height: 56 }, label: 'Bezier' },
{ id: 'h', position: { x: 570, y: 600 }, size: { width: 140, height: 56 }, label: 'Target' },
{ id: 'i', position: { x: 760, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross A' },
{ id: 'j', position: { x: 1040, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross D' },
{ id: 'k', position: { x: 760, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross B' },
{ id: 'l', position: { x: 1040, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross C' },
];
const edges: EdgeSpec[] = [
{ id: 'direct', source: 'a', target: 'b', type: 'direct', label: 'direct', labelPlacement: 'above', labelStyle: { color: '#1d4ed8', fontSize: 12 }, style: { stroke: '#2563eb', strokeWidth: 2, arrowHead: { type: 'arrow', size: 14, filled: true } } },
{ id: 'sharp', source: 'c', target: 'd', router: 'orthogonal', connector: 'straight', label: 'sharp corners', labelPlacement: 'above', style: { stroke: '#475569', strokeDasharray: '6 3', arrowHead: { type: 'diamond', size: 14, filled: false } } },
{ id: 'rounded', source: 'e', target: 'f', router: 'orthogonal', connector: 'rounded', label: 'rounded corners', labelPlacement: 'below', labelStyle: { background: '#eff6ff', padding: 4, borderRadius: 4 }, style: { stroke: '#059669', strokeWidth: 2, arrowHead: { type: 'crow-foot', size: 14, filled: false } } },
{ id: 'curve', source: 'g', target: 'h', type: 'bezier', label: 'no arrowhead', style: { stroke: '#9333ea', strokeWidth: 2, arrowHead: { type: 'none', size: 14, filled: false } } },
{ id: 'cross-ad', source: 'i', target: 'l', type: 'direct', style: { stroke: '#0f766e', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
{ id: 'cross-bc', source: 'k', target: 'j', type: 'direct', style: { stroke: '#b91c1c', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } },
];
export default function EdgeStyles() {
return (
<div style={{ height: '900px' }}>
<GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} />
</div>
);
}
vue<script setup lang="ts"> import { GrafloriaFlow } from '@grafloria/vue'; import type { EdgeSpec } from '@grafloria/renderer'; const nodes = [ { id: 'a', position: { x: 80, y: 80 }, size: { width: 140, height: 56 }, label: 'Direct' }, { id: 'b', position: { x: 570, y: 150 }, size: { width: 140, height: 56 }, label: 'Target' }, { id: 'c', position: { x: 80, y: 230 }, size: { width: 140, height: 56 }, label: 'Sharp step' }, { id: 'd', position: { x: 570, y: 300 }, size: { width: 140, height: 56 }, label: 'Target' }, { id: 'e', position: { x: 80, y: 380 }, size: { width: 140, height: 56 }, label: 'Rounded step' }, { id: 'f', position: { x: 570, y: 450 }, size: { width: 140, height: 56 }, label: 'Target' }, { id: 'g', position: { x: 80, y: 530 }, size: { width: 140, height: 56 }, label: 'Bezier' }, { id: 'h', position: { x: 570, y: 600 }, size: { width: 140, height: 56 }, label: 'Target' }, { id: 'i', position: { x: 760, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross A' }, { id: 'j', position: { x: 1040, y: 680 }, size: { width: 120, height: 48 }, label: 'Cross D' }, { id: 'k', position: { x: 760, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross B' }, { id: 'l', position: { x: 1040, y: 840 }, size: { width: 120, height: 48 }, label: 'Cross C' }, ]; const edges: EdgeSpec[] = [ { id: 'direct', source: 'a', target: 'b', type: 'direct', label: 'direct', labelPlacement: 'above', labelStyle: { color: '#1d4ed8', fontSize: 12 }, style: { stroke: '#2563eb', strokeWidth: 2, arrowHead: { type: 'arrow', size: 14, filled: true } } }, { id: 'sharp', source: 'c', target: 'd', router: 'orthogonal', connector: 'straight', label: 'sharp corners', labelPlacement: 'above', style: { stroke: '#475569', strokeDasharray: '6 3', arrowHead: { type: 'diamond', size: 14, filled: false } } }, { id: 'rounded', source: 'e', target: 'f', router: 'orthogonal', connector: 'rounded', label: 'rounded corners', labelPlacement: 'below', labelStyle: { background: '#eff6ff', padding: 4, borderRadius: 4 }, style: { stroke: '#059669', strokeWidth: 2, arrowHead: { type: 'crow-foot', size: 14, filled: false } } }, { id: 'curve', source: 'g', target: 'h', type: 'bezier', label: 'no arrowhead', style: { stroke: '#9333ea', strokeWidth: 2, arrowHead: { type: 'none', size: 14, filled: false } } }, { id: 'cross-ad', source: 'i', target: 'l', type: 'direct', style: { stroke: '#0f766e', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } }, { id: 'cross-bc', source: 'k', target: 'j', type: 'direct', style: { stroke: '#b91c1c', jumpPoints: { enabled: true, size: 10, detectMode: 'all' } } }, ]; </script> <template> <div style="height:900px"> <GrafloriaFlow :default-nodes="nodes" :default-edges="edges" /> </div> </template>
The first four rows render four distinct path families. router: 'orthogonal' keeps both step routes axis-aligned; changing connector from 'straight' to 'rounded' changes their corner treatment without changing that routing choice. Each row also renders its own label and marker style. The two lower edges cross, and one draws a small hop at the crossing.
Place and style labels
Set label to put text on an edge. labelPlacement: 'on' places it on the line in its own box; 'above' and 'below' place it beside the line without that box. labelStyle controls the label's color, size, weight, family, and background. In the example, the direct edge's label has blue text and sits above its line, while the rounded route's label has a pale background and sits below.
Labels belong to the edge path: when its route changes, the label follows the route. In the live label demo, drag the label along the wire, then move a node to see the edge and label re-route together.
Choose edge markers
Set style.arrowHead to a built-in marker type. The sample uses arrow, diamond, and crow-foot; none explicitly renders no arrowhead. The head stays at the endpoint as the route changes. The live marker demo shows the full built-in set: arrow, open-arrow, circle, square, diamond, crow-foot, hollow-diamond, and one-or-many, as well as none.
Show crossings
Give crossing edges style.jumpPoints with enabled: true to draw an arc where a line crosses another. The lower pair in the sample starts in an X, so the owning edge renders a hop. Move a node until the lines no longer cross and the hop disappears; move it back and the hop returns. size controls the arc size in pixels, and detectMode: 'all' selects all crossings.
Options used here
| Option | Type | Default | What it does |
|---|---|---|---|
type | 'direct' | 'smooth' | 'orthogonal' | 'bezier' | Not specified | Selects a path family. |
router | LinkRouterName | Derived from type when unset | Chooses where the line goes. |
connector | LinkConnectorName | Derived from type when unset | Chooses how the route is drawn. |
label | string | Not specified | Adds text to the edge. |
labelPlacement | 'on' | 'above' | 'below' | 'on' | Places the label on, above, or below the edge. |
labelStyle | LabelStyle | Not specified | Sets label color, size, weight, family, and background. |
style | Partial<LinkStyle> | Not specified | Sets the link's visual style, including stroke and arrowheads. |
style.jumpPoints | JumpPointConfig | Disabled unless enabled | Controls edge hops at crossings. size defaults to 10 px and detectMode to 'all'. |
These values live on the edge spec; you do not need to reach into the model to style a link. The route and connector answer different questions, so changing the connector does not select a new router. For more routing options and saved bends, see Route and edit edges.
Was this page helpful?