Routes

The shape of a dependency's line: a curve that leaves and arrives forwards, a straight line, or orthogonal segments. Set it for the schedule, or for one dependency.

import { useSchedule } from "@umriss-ui/schedule";

Examples

Curve

The default: each line leaves forwards and arrives forwards, so it shows which way the work goes, even when it loops back up.

import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";

const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;

const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];

const WORK: Subtask[] = [
  { id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
  { id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
  { id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
  { id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];

const HANDOVERS: Dependency[] = [
  { id: "to-build", from: "layout", to: "build", lag: min(20) },
  { id: "to-test", from: "build", to: "test", lag: min(20) },
  { id: "to-polish", from: "test", to: "polish", lag: min(20) },
];

export default function Curve() {
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
  return (
    <Schedule ariaLabel="Search results, from layout to polish, curved lines" height={188}>
      <Lane id="noah" label="Noah Fischer" />
      <Lane id="chloe" label="Chloe Durand" />
      <Lane id="eva" label="Eva Novak" />
      <Dependencies data={HANDOVERS} />
      <Subtasks data={WORK} tasks={PROJECTS} />
    </Schedule>
  );
}

Straight

route="straight" draws the shortest line between the two ends, calm in a plan read from a distance.

import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";

const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;

const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];

const WORK: Subtask[] = [
  { id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
  { id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
  { id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
  { id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];

const HANDOVERS: Dependency[] = [
  { id: "to-build", from: "layout", to: "build", lag: min(20) },
  { id: "to-test", from: "build", to: "test", lag: min(20) },
  { id: "to-polish", from: "test", to: "polish", lag: min(20) },
];

export default function Straight() {
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
  return (
    <Schedule ariaLabel="Search results, from layout to polish, straight lines" height={188} route="straight" attach="nearest">
      <Lane id="noah" label="Noah Fischer" />
      <Lane id="chloe" label="Chloe Durand" />
      <Lane id="eva" label="Eva Novak" />
      <Dependencies data={HANDOVERS} />
      <Subtasks data={WORK} tasks={PROJECTS} />
    </Schedule>
  );
}

Orthogonal

route="orthogonal" leaves the bar, runs across and comes in again, like a flow chart.

import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";

const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;

const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];

const WORK: Subtask[] = [
  { id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
  { id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
  { id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
  { id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];

const HANDOVERS: Dependency[] = [
  { id: "to-build", from: "layout", to: "build", lag: min(20) },
  { id: "to-test", from: "build", to: "test", lag: min(20) },
  { id: "to-polish", from: "test", to: "polish", lag: min(20) },
];

export default function Orthogonal() {
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
  return (
    <Schedule ariaLabel="Search results, from layout to polish, orthogonal lines" height={188} route="orthogonal" attach="nearest">
      <Lane id="noah" label="Noah Fischer" />
      <Lane id="chloe" label="Chloe Durand" />
      <Lane id="eva" label="Eva Novak" />
      <Dependencies data={HANDOVERS} />
      <Subtasks data={WORK} tasks={PROJECTS} />
    </Schedule>
  );
}

Per dependency

A route on a single dependency overrides the schedule's: here only the loop back up to the designer is drawn orthogonal.

import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";

const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;

const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];

const WORK: Subtask[] = [
  { id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
  { id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
  { id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
  { id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];

const HANDOVERS: Dependency[] = [
  { id: "to-build", from: "layout", to: "build", lag: min(20) },
  { id: "to-test", from: "build", to: "test", lag: min(20) },
  /* Its own route, whatever the schedule says. */
  { id: "to-polish", from: "test", to: "polish", lag: min(20), route: "orthogonal" },
];

export default function PerDependency() {
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
  return (
    <Schedule ariaLabel="Search results, from layout to polish, straight lines but one" height={188} route="straight" attach="nearest">
      <Lane id="noah" label="Noah Fischer" />
      <Lane id="chloe" label="Chloe Durand" />
      <Lane id="eva" label="Eva Novak" />
      <Dependencies data={HANDOVERS} />
      <Subtasks data={WORK} tasks={PROJECTS} />
    </Schedule>
  );
}

Keyboard

The keys of First schedule apply here.

Props on this page

From ScheduleProps — the full table stands on First schedule.

NameTypeDefaultDescription
heightnumber400Height in CSS pixels, both bands included. The width is the host's.
routeDependencyRoute
| "curve"
| "straight"
| "orthogonal"
"curve"How the dependencies are drawn: a "curve" that leaves and arrives forwards, a "straight" line, or "orthogonal" segments. A dependency may say otherwise for itself.
attachDependencyAttachment
"centre" | "nearest"
"centre"Where a dependency's ends sit on their bars: the "centre" of both, or the "nearest" edge - which is the shortest line between two stops. Within one lane both mean the middle. A dependency may say otherwise for itself. It changes the picture and never a finding: whether a dependency is violated follows from its leaves and arrives alone.
childrenReactNode—Lane, Dependencies and Subtasks, in the order they are to stand and be drawn.

Accessibility

NameTypeDefaultDescription
ariaLabelrequiredstring—What the schedule shows, for a screen reader: "Plan of week 38".

From LaneProps — the full table stands on Lanes.

NameTypeDefaultDescription
idrequiredstring—The identity subtasks name in their lane.
labelReactNodeidWhat the lane header says - the person, vehicle or room. Real text, read by a screen reader.

From SubtasksProps — the full table stands on Subtasks.

NameTypeDefaultDescription
datarequiredreadonly Subtask[]—The subtasks, as the caller holds them. The schedule never changes one.
tasksrequiredreadonly Task[]—The tasks the subtasks belong to - their identity and their colour. A subtask whose task is not among them is drawn in a muted colour.

From Subtask — the full table stands on Subtasks.

NameTypeDefaultDescription
idrequiredstring—The caller's identity of the subtask.
taskrequiredstring—The id of the task it belongs to.
lanerequiredstring—The id of the lane it sits on.
fromrequirednumber—Start of the main time.
torequirednumber—End of the main time.
namestring—What the subtask is called, where the schedule names it.

From Task — the full table stands on Subtasks.

NameTypeDefaultDescription
idrequiredstring—The caller's identity of the task.
colorrequiredstring—Any CSS colour - a literal, a token, light-dark(…). The colour means what the caller's domain needs it to mean.
namestring—What the task is called, where the schedule names it.

From DependenciesProps — the full table stands on Dependencies.

NameTypeDefaultDescription
datarequiredreadonly Dependency[]—The dependencies, as the caller holds them.

From Dependency — the full table stands on Dependencies.

NameTypeDefaultDescription
idrequiredstring—The caller's identity of the dependency.
fromrequiredstring—The id of the subtask it leaves.
torequiredstring—The id of the subtask it arrives at.
lagrequirednumber—The least time between the two anchors; zero where the successor may begin as soon as the predecessor ends.
routeDependencyRoute
| "curve"
| "straight"
| "orthogonal"
—How this one is drawn, where it is not drawn like the rest. It changes the picture and never the finding: whether a dependency is violated follows from leaves and arrives alone.

From ScheduleOptions — the full table stands on View.

NameTypeDefaultDescription
initialViewScheduleView—The view to start from, read once when the hook first runs; a different value on a later render is ignored, as React ignores a changed defaultValue. To go to a view later, call setView. The schedule remembers none: where a view is kept is the application's decision.

From ScheduleView — the full table stands on View.

NameTypeDefaultDescription
domainreadonly [number, number]—The span in view, as two wall-clock instants. Absent, the schedule shows the extent of its subtasks, within zoomLimits.

Every page of @umriss-ui/schedule

Getting started

Plan

Time

Reading

Editing

Measuring

API index