Skip to content
D
Documentation

Route and edit edges

how-to
2 min readUpdated

Use an edge's route to express how it travels, then let the mounted diagram keep that route correct as nodes move. This page covers geometry, obstacle avoidance, labels, waypoints, and reconnecting an existing edge in JavaScript, Angular, Qwik, React, and Vue.

Choose the route and shape

For the EdgeSpec fields source, target, type, and router, see layout and routing. This page adds connector styling, labels, fixed waypoints, obstacle avoidance, and endpoint reconnection.

ts
import type { EdgeSpec } from '@grafloria/renderer';

const edges: EdgeSpec[] = [{
  id: 'avoiding-edge', source: 'a', target: 'b', router: 'avoid', connector: 'rounded',
  label: 'retry', labelPlacement: 'above', waypoints: [{ x: 420, y: 180 }],
}];

Use connector: 'straight' | 'rounded' | 'smooth' | 'bezier' for the connector treatment. A label is editable in the mounted diagram; labelPlacement: 'above' or 'below' places it beside the line, and labelStyle controls its appearance.

Mount an obstacle-avoiding edge

The following data puts a wall between A and B. The edge renders as a right-angled detour and stays outside the wall when the wall moves. Give the host a height: a zero-height host has no canvas to paint.

js
import { render } from '@grafloria/element';
const nodes = [
  { id: 'a', position: { x: 80, y: 260 }, size: { width: 120, height: 60 }, label: 'A' },
  { id: 'b', position: { x: 700, y: 260 }, size: { width: 120, height: 60 }, label: 'B' },
  { id: 'wall', position: { x: 390, y: 205 }, size: { width: 140, height: 170 }, label: 'obstacle' },
];
const edges = [{ id: 'e1', source: 'a', target: 'b', router: 'avoid', connector: 'rounded' }];
const host = document.getElementById('diagram');
if (!host) throw new Error('Missing #diagram');
host.style.height = '100vh';
const api = render({ nodes, edges }, host, { interaction: { enableWaypointEditing: true, showWaypointHandles: true } });
api.renderNow();

The JavaScript render call returns the live DiagramInstance. The wrappers mount the same model; moving wall makes the obstacle-avoiding route recalculate. See the edge-routing demo.

Add labels and fixed bends

Put interior points in waypoints when a route must pass through known bends. Points use world coordinates; do not include endpoint coordinates. The route keeps the bends while its endpoints reattach as nodes move.

ts
import type { EdgeSpec } from '@grafloria/renderer';

const labelledEdge: EdgeSpec = {
  id: 'labelled', source: 'a', target: 'b', router: 'orthogonal', connector: 'rounded',
  waypoints: [{ x: 360, y: 160 }, { x: 620, y: 160 }], label: 'asks for approval',
  labelPlacement: 'above', labelStyle: { color: '#5f6b7a', fontSize: 11 },
};

For interactive editing, enable enableWaypointEditing and showWaypointHandles in the interaction configuration. Select an edge, click its path to add one interior waypoint, then drag the handle. Each bend changes the rendered route and is one undoable user edit. See the editable-edge demo.

js
import { render } from '@grafloria/element';
const nodes = [{ id: 'a', position: { x: 80, y: 180 }, label: 'A' }, { id: 'b', position: { x: 620, y: 180 }, label: 'B' }];
const host = document.getElementById('diagram');
if (!host) throw new Error('Missing #diagram');
host.style.height = '100vh';
const editable = render({ nodes, edges: [{ id: 'e1', source: 'a', target: 'b', type: 'direct' }] }, host, { interaction: { enableWaypointEditing: true, showWaypointHandles: true } });
editable.renderNow();

Reconnect an existing edge

Pin an endpoint to a side or port when the connection must land there:

ts
import type { EdgeSpec } from '@grafloria/renderer';

const reconnectable: EdgeSpec = {
  id: 'e1', source: 'a', target: 'b', sourceHandle: 'right', targetHandle: 'left', type: 'direct',
};

With waypoint editing off, select the edge, press its endpoint handle, drag to another valid port, and release. The model changes from A→B to A→C; validators run for the new target. Subscribe to the instance's reconnect event when application state needs the new endpoint:

ts
import { render } from '@grafloria/element';

const nodes = [
  { id: 'a', position: { x: 80, y: 260 }, label: 'A' },
  { id: 'b', position: { x: 660, y: 110 }, label: 'B' },
  { id: 'c', position: { x: 660, y: 430 }, label: 'C' },
];
const host = document.getElementById('diagram');
if (!host) throw new Error('Missing #diagram');
host.style.height = '100vh';
const api = render({ nodes, edges: [{ id: 'e1', source: 'a', target: 'b', sourceHandle: 'right', targetHandle: 'left', type: 'direct' }] }, host);
const stop = api.on('reconnect', () => {
  const link = api.getModel().getLink('e1');
  if (link) console.log(link.targetNodeId);
});

// Call stop() when the host unmounts.

The same gesture is built into the bindings shown here; no custom pointer handler is required. Keep endpoint handles pinned in a test or demo when you need the handle to sit directly on the port. See the reconnect-edge demo.

Pitfalls

  • type chooses the line shape; router chooses the path. Set both when you need both guarantees.
  • waypoints contains interior points only. The router supplies the endpoints.
  • Waypoint editing is opt-in. Without enableWaypointEditing, clicking the path does not add a bend.
  • A mounted canvas needs a resolved height. See edit Mermaid diagrams for the canvas sizing warning.
  • Reconnecting an endpoint invokes connection validation again. See ports and connection rules.

Was this page helpful?