Skip to content

Link types and lags

A link joins an edge of one task (the source) to an edge of another (the target) and keeps them lag apart. There are four types, one for each pairing of edges. Here one task, Design, is the source of nine links: two of each type, one with a lag (a gap, positive) and one with a lead (a negative lag, an overlap), and one link that names neither a type nor a lag. Drag Design and every target moves with it, each by its own rule.

Terminal window
pnpm add @ganttlane/dom # vanilla
pnpm add @ganttlane/react react react-dom # React
View on GitHub
data.ts
import type { Link, Task } from '@ganttlane/core';
// One source, Design (Monday 2026-03-09 to Thursday 2026-03-12), and nine tasks linked to it: two for
// each link type, one with a lag (a gap after the anchor, positive) and one with a lead (a
// negative lag: the anchors overlap), and one whose link names neither a type nor a lag.
//
// A link joins an edge of the source to an edge of the target, `lag` apart:
// FS the end of the source to the start of the target
// SS the start of the source to the start of the target
// FF the end of the source to the end of the target
// SF the start of the source to the end of the target
// A task with an `end` ends on that day (inclusive); a task with a `duration` lasts that many days.
export const tasks: Task[] = [
{ id: 'design', name: 'Design', start: '2026-03-09', end: '2026-03-12' },
// FS: starts 2 days after Design ends, and 2 days before it ends.
{ id: 'build', name: 'Build · FS +2d', start: '2026-03-15', duration: 3 },
{ id: 'docs', name: 'Docs · FS -2d', start: '2026-03-11', duration: 3 },
// SS: starts 2 days after Design starts, and 2 days before it starts.
{ id: 'frontend', name: 'Frontend · SS +2d', start: '2026-03-11', duration: 3 },
{ id: 'qa', name: 'QA · SS -2d', start: '2026-03-07', duration: 3 },
// FF: ends 2 days after Design ends, and 2 days before it ends.
{ id: 'marketing', name: 'Marketing · FF +2d', start: '2026-03-11', end: '2026-03-14' },
{ id: 'release', name: 'Release · FF -2d', start: '2026-03-07', end: '2026-03-10' },
// SF: ends 2 days after Design starts, and 2 days before it starts.
{ id: 'hiring', name: 'Hiring · SF +2d', start: '2026-03-07', end: '2026-03-10' },
{ id: 'support', name: 'Support · SF -2d', start: '2026-03-04', end: '2026-03-06' },
// No type and no lag: the type is `scheduling.defaultLinkType`, the lag the gap it has now.
{ id: 'review', name: 'Review · no type', start: '2026-03-15', duration: 3 },
];
export const links: Link[] = [
{ source: 'design', target: 'build', type: 'FS', lag: 2 },
{ source: 'design', target: 'docs', type: 'FS', lag: -2 },
{ source: 'design', target: 'frontend', type: 'SS', lag: 2 },
{ source: 'design', target: 'qa', type: 'SS', lag: -2 },
{ source: 'design', target: 'marketing', type: 'FF', lag: 2 },
{ source: 'design', target: 'release', type: 'FF', lag: -2 },
{ source: 'design', target: 'hiring', type: 'SF', lag: 2 },
{ source: 'design', target: 'support', type: 'SF', lag: -2 },
{ source: 'design', target: 'review' },
];

Files

  • data.ts: Design, its nine targets and the links, with the dates worked out in comments.
  • options.ts: the calendar, scheduling.defaultLinkType, display.linkCurveRadius.
  • helpers.ts: describe, which turns the ChangeSet of a drag into a sentence.
  • vanilla.ts and react.tsx: the chart, two selects and a reset button.

Options

Option Value What it does
calendar.weekends [] Every day is a working day: no weekend fill, so only the links move the bars
scheduling.defaultLinkType 'FS' The type of a link that names none (the select changes it: setOptions)
display.linkCurveRadius 10 The largest radius, in px, of the corners of a link’s route (default 5)
layout.rowHeight, barHeight 36, 22 Compact rows, so that all ten show
scroll.initial 'first-task' Opens on the plan; the default, 'today', is far from these dates
onTasksChange a function (in the example) One ChangeSet per drag, with the dragged task and every task it moved
link.type, link.lag 'FS', 2, -2, … Per link: which edges, and how far apart (a number is days; a string such as '4h' is a duration)

What to try

  • Drag Design to the right. All nine targets move by the same number of days: a link keeps its lag whichever way the source goes. The status line lists them in the order the chart moved them.
  • Drag the right edge of Design. Only the targets that hang from its end move: the FS ones (their start) and the FF ones (their end). The SS and SF ones stay, because they hang from its start.
  • Drag the left edge of Design. Now the SS and SF targets move, and the FS and FF ones stay. A link on an edge that did not move does not move its target.
  • Drag a target (say Build) closer to Design than its lag allows. A target is free to go anywhere: nothing pushes it back, but the link turns red and the status line reports the conflict. Drag Design again and the link puts Build back exactly where its lag says.
  • Change the default link type. Review has a link with no type and no lag: it takes the type from the select, and the lag from the gap its two edges have now, so the arrow is redrawn and nothing moves until you drag.
  • Change the corner radius and watch the routes.

Notes

  • The four types. FS joins the end of the source to the start of the target, SS the start to the start, FF the end to the end, SF the start of the source to the end of the target.
  • The lag is a gap between those two edges, counted in the target’s durationMode (calendar days here). A negative lag is a lead: the target’s edge lies before the source’s. A number is days; a string is a duration ('3d', '4h').
  • A lag is a rule, not a minimum. When the source moves, the target goes to exactly lag from it, in both directions. A link with no lag keeps the gap it has when the data is loaded.
  • A link to an edge that does not move moves nothing. Resizing the start of the source leaves the targets of its FS and FF links alone.
  • Two links to the same target pin both of its edges. An SS and an FF link make the target stretch when the source is resized. An FS and an FF link both hang from the source’s end, so with no lags (each keeps the gap it has) the target moves with it and keeps its length. A child pinned to both edges of its phase (['SS', 'FF'], with followParent) stretches the same way, see Groups and children.
  • React. scheduling and display are passed to <Gantt> after the spread of options, from state: in React the props are the truth, so an option changed with setOptions on the ref would be overwritten by the next render. defaultTasks and defaultLinks are where the chart starts, and chart.current.setTasks(tasks) puts the data back.