Lanes

One row per person, vehicle or room, named by its header and kept in the order it is declared, however many there are (also called resources).

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

A lane is declared by its id, the one subtasks name, and a label. The label is content, not a string: a name with a plate number, a status beside it. It is real text, read by a screen reader, and it stays at the left edge while the plot pans.

A lane keeps its height whatever lies on it. Nothing is moved to another lane to make room: two subtasks on one lane at once are a finding (see Overlap).

Examples

What a lane header says

A label is content, not a string: here the vehicle with its plate, and a badge for the van that is off the road.

import { Badge, Stack, Text } from "@umriss-ui/core";
import { useSchedule } from "@umriss-ui/schedule";
import type { Subtask, Task } from "@umriss-ui/schedule";

const at = (hours: number) => new Date(2026, 2, 17, hours).getTime();

const TOURS: Task[] = [{ id: "t-02", name: "T-02 Northfold", color: "light-dark(#0d9488, #3cc7b8)" }];

const LEGS: Subtask[] = [
  { id: "t-02-1", task: "t-02", lane: "v1", from: at(7), to: at(9) },
  { id: "t-02-2", task: "t-02", lane: "v2", from: at(9), to: at(11) },
];

export default function LaneHeaders() {
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(12)] } });
  return (
    <Schedule ariaLabel="Two vans of the North depot" height={150} headerWidth={210}>
      <Lane
        id="v1"
        label={
          <Stack direction="row" gap={1} align="center">
            <Text size="sm">Van</Text>
            <Text size="xs" tone="muted" mono>
              FP 214 K
            </Text>
          </Stack>
        }
      />
      <Lane
        id="v2"
        label={
          <Stack direction="row" gap={1} align="center" wrap>
            <Text size="sm">E-van</Text>
            <Text size="xs" tone="muted" mono>
              FP 377 K
            </Text>
            <Badge tone="warning">Workshop</Badge>
          </Stack>
        }
      />
      <Subtasks data={LEGS} tasks={TOURS} />
    </Schedule>
  );
}

Scroll more lanes than fit

Twenty vans in 300 pixels: the lanes scroll with their headers, while the time bands and the header column hold still.

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

/* `height` is the whole schedule, both bands included; `laneHeight` and
   `headerWidth` set the rest. */

const DEPOTS: Task[] = [
  { id: "north", name: "North depot", color: "light-dark(#2563eb, #6b9bff)" },
  { id: "river", name: "Riverside depot", color: "light-dark(#0d9488, #3cc7b8)" },
];

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

const VANS = Array.from({ length: 20 }, (_, i) => ({ id: `van-${i + 1}`, label: `Van ${String(i + 1).padStart(2, "0")}` }));

/* One round per van, each starting a quarter hour after the one above. */
const ROUNDS: Subtask[] = VANS.map((van, i) => ({
  id: `${van.id}-round`,
  task: i % 2 === 0 ? "north" : "river",
  lane: van.id,
  from: at(7, 15 * i),
  to: at(9, 15 * i),
  leadIn: 15 * 60_000,
}));

export default function ManyLanes() {
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(14)] } });
  return (
    <Schedule
      ariaLabel="Twenty vans, Tuesday morning"
      height={300}
      laneHeight={32}
      headerWidth={110}
    >
      {VANS.map((van) => (
        <Lane key={van.id} id={van.id} label={van.label} />
      ))}
      <Subtasks data={ROUNDS} tasks={DEPOTS} />
    </Schedule>
  );
}

When to use something else

Keyboard

The keys of First schedule apply here.

API

LaneProps

NameTypeDefaultDescription
idrequiredstring—The identity subtasks name in their lane.

Shown in: What a lane header says, Scroll more lanes than fit, The smallest schedule (Installation) and 78 more

labelReactNodeidWhat the lane header says - the person, vehicle or room. Real text, read by a screen reader.

Shown in: What a lane header says, Scroll more lanes than fit, The smallest schedule (Installation) and 77 more

Every page of @umriss-ui/schedule

Getting started

Plan

Time

Reading

Editing

Measuring

API index