TimelineKit

Data

Tasks & Milestones

Tasks are the core data unit. Each task has a type that determines its behavior and appearance.

Task Types

taskA regular task with start/end time and duration.
milestoneA zero-duration point in time (only startTime).
summaryA parent task whose dates are derived from its subtasks.
emptyA placeholder row with no dates.

Task Properties

id: stringUnique identifier (read-only).
name: string | nullDisplay name of the task.
type: TaskType'task' | 'milestone' | 'summary' | 'empty'
startTime: Date | nullStart date/time.
endTime: Date | nullEnd date/time.
length: Length | nullDuration (e.g. 5d, 8h). Parsed from string via Length.tryParse().
progress: number | nullCompletion percentage (0–100).
customLengthUnit: LengthUnit | nullOverride for the length display unit. When null, the calendar's default unit is used.
color: ColorValueColor index into the theme's taskColors array, or a custom ColorDefinition.
note: string | nullFree-text note / description.
work: Work | nullTotal work effort (e.g. 40h, 5d).
cost: number | nullTotal cost computed from resource rates and work.
constraintType: ConstraintTypeScheduling constraint type (default: 'asap').
constraintDate: Date | nullTarget date for the constraint.
baselineStartTime: Date | nullBaseline start date (set by saveBaseline).
baselineEndTime: Date | nullBaseline end date (set by saveBaseline).
collapsed: booleanWhether a summary task's subtasks are collapsed.
warnings: string[]Validation warnings (e.g. scheduling conflicts).
filtered: booleanWhether the task is excluded by the active filter.
hidden: booleanWhether the task is hidden (collapsed parent or filtered). Read-only.
parent: Task | nullParent task (read-only). Null for top-level tasks.
level: numberNesting depth in hierarchy (read-only).

Task Colors

Each task has a color property that controls its visual appearance in the chart. Set it to a theme color index (0–7) or a custom ColorDefinition. The default themes provide 8 color sets: blue (0), red (1), green (2), yellow (3), purple (4), orange (5), teal (6), pink (7).

const task1 = gantt.list.getTaskById('1');
const task2 = gantt.list.getTaskById('2');
const task3 = gantt.list.getTaskById('3');

// Use theme color index (0–7)
task1.color = 0; // blue (default)
task2.color = 1; // red
task3.color = 2; // green

// Or use a fully custom color definition
task1.color = {
  color1: '#dbeafe',       // bar gradient top
  color2: '#3b82f6',       // bar gradient bottom
  highlightColor: 'rgba(59, 130, 246, 0.3)', // hover overlay
  borderColor: '#2563eb',  // border (null = no border)
  progressColor1: '#2563eb', // progress gradient top
  progressColor2: '#1d4ed8', // progress gradient bottom
};

// Color-code tasks by priority
for (let i = 0; i < gantt.list.length; i++) {
  const task = gantt.list.getTask(i);
  const priority = task.getPropertyValue('priority');
  if (priority === 'High') task.color = 1;       // red
  else if (priority === 'Medium') task.color = 5; // orange
  else task.color = 0;                            // blue (default)
}

To customize the color palette itself (e.g. change what index 0 looks like), see the Theming & Styles guide.

Hierarchy

Tasks support parent/child relationships. Use indent() and outdent() to change nesting, or manipulate programmatically:

// Add a subtask
const parent = gantt.list.getTaskById('1');
const child = gantt.list.getTaskById('2');
parent.addSubtask(child);

// Remove a subtask (promotes it to sibling)
parent.removeSubtask(child);

// Access subtasks
parent.subtasksLength; // number of direct children
parent.getSubtask(0);  // first child

// Collapse / expand
parent.toggleCollapsed();

// Indent / outdent selected tasks
gantt.indent();
gantt.outdent();

// Move tasks up / down
gantt.moveUp();
gantt.moveDown();

Task Parts

A task can be split into multiple parts (e.g. interrupted work). By default, users can create new parts by dragging on an empty area of an existing task row. Disable this with allowTaskSplitting:

// Disable creating new parts via mouse (programmatic addPart still works)
gantt.allowTaskSplitting = false;

const task = gantt.list.getTaskById('1');

// Add a second part
task.addPart(new Date('2027-01-20'), Length.parse('3d'));

// Access parts
console.log(task.partsLength); // 2
const part = task.getPart(1);
console.log(part.startTime, part.endTime);

// Remove a part
task.removePart(part);

Populating the Chart (Programmatic)

The recommended way to populate the chart is the programmatic API. Use createTask() to create a properly initialized task, set its properties, then add it with addTask(). Date setters automatically normalize values to working hours:

const list = gantt.list;
list.clear(); // remove default empty rows

const design = list.createTask('1');
design.name = 'Design';
design.startTime = new Date('2027-01-05'); // normalized to start of shift (e.g. 08:00)
design.endTime = new Date('2027-01-09');   // normalized to end of shift (e.g. 17:00)
design.progress = 100;
list.addTask(design);

const dev = list.createTask('2');
dev.name = 'Development';
dev.startTime = new Date('2027-01-12');
dev.endTime = new Date('2027-01-23');
list.addTask(dev);

// Add a dependency
list.addLink(design, dev, 'finishToStart');

Serialization (load / save)

Use load() and save()to persist and restore chart state (e.g. to/from a database, file, or local storage). These methods are designed as counterparts — load() expects JSON previously produced by save():

Date-only values (e.g. "2027-01-05") are also accepted and automatically normalized to working hours: startTime is moved to the beginning of the first shift (e.g. 08:00) and endTime to the end of the last shift (e.g. 17:00). Milestones are normalized to end of the working day. This makes it easy to hand-write JSON without specifying exact times.

// Save current state
const json = gantt.save();
localStorage.setItem('gantt-data', json);

// Restore saved state
const saved = localStorage.getItem('gantt-data');
if (saved) {
  gantt.load(saved);
}

Adding & Removing Tasks

Additional ways to add and manage tasks:

// Or add an empty row and fill it in
const empty = gantt.list.addEmptyTask();
empty.name = 'Planning';
empty.startTime = new Date('2027-01-05');
empty.endTime = new Date('2027-01-09');

// Insert an empty task at a specific position
gantt.list.insertEmptyTask(2);

// Access tasks by index or ID
const first = gantt.list.getTask(0);
const task1 = gantt.list.getTaskById('1');
console.log(gantt.list.length); // total task count

// Find tasks using a specific resource
const tasks = gantt.list.getTasksUsingResource(dev);

// Remove
gantt.list.removeTask(task);
gantt.list.removeTasks([task1, task2]);

// Clear all
gantt.list.clear();

Dependencies

Dependencies (links) define scheduling relationships between tasks. When a predecessor changes, dependent tasks are automatically rescheduled.

Link Types

finishToStart (FS)Task B starts after Task A finishes. The most common type.
startToStart (SS)Task B starts when Task A starts.
finishToFinish (FF)Task B finishes when Task A finishes.
startToFinish (SF)Task B finishes when Task A starts.
// Add a dependency
const from = gantt.list.getTaskById('1');
const to = gantt.list.getTaskById('2');
const link = gantt.list.addLink(from, to, 'finishToStart');

// Add with lag (delay) or lead (negative lag)
const lag = Lag.parse('+2d');
gantt.list.addLink(from, to, 'finishToStart', lag);

// Remove a dependency
gantt.list.removeLink(link);

// Check before adding
gantt.list.existsLinkBetween(from, to);  // already linked?
gantt.list.wouldCreateCycle(from, to);    // would create cycle?

// Determine link type from connector sides
import { getLinkType } from '@timelinekit/core';
const type = getLinkType('finish', 'start'); // 'finishToStart'

Auto-scheduling

Dependencies are automatically enforced by the scheduling engine. When a predecessor task changes (moved, resized, or its duration changes), all dependent successors are automatically rescheduled to maintain the dependency relationship. This cascade propagates through the entire dependency chain — if Task A → Task B → Task C, moving Task A automatically shifts both B and C.

The scheduling engine also respects constraints and the working calendar — successors are placed on the next available working time after the dependency is satisfied. Tasks with constraints (e.g. "Start No Earlier Than") will not be moved before their constraint date, and a warning is generated if a dependency conflict arises.

// When you move Task A, Task B is automatically rescheduled:
const taskA = gantt.list.getTaskById('1');
const taskB = gantt.list.getTaskById('2');
gantt.list.addLink(taskA, taskB, 'finishToStart');

// Moving Task A's end date forward → Task B's start shifts automatically
taskA.endTime = new Date('2027-01-15');
// taskB.startTime is now automatically >= 2027-01-15 (next working day)

// With lag: Task B starts 2 days after Task A finishes
gantt.list.addLink(taskA, taskB, 'finishToStart', Lag.parse('+2d'));

Link Properties

id: stringUnique identifier (read-only).
from: TaskSource (predecessor) task (read-only).
to: TaskTarget (successor) task (read-only).
type: TaskLinkType'finishToStart' | 'startToStart' | 'finishToFinish' | 'startToFinish'
lag: LagTime offset. Positive = delay, negative = lead.

Resources

Resources represent people, equipment, or materials assigned to tasks. Each assignment has a capacity (allocation percentage).

// Define resources
const dev = new Resource(null, 'Alice', 'unique');
const designer = new Resource(null, 'Bob', 'unique');
gantt.resources.addResource(dev);
gantt.resources.addResource(designer);

// Look up a resource
const res = gantt.resources.getResourceById('r1');
const first = gantt.resources.getResource(0);
console.log(gantt.resources.length);

// Remove a resource
gantt.resources.removeResource(dev);

// Assign a resource to a task
const task = gantt.list.getTaskById('1');
task.addResource(null, dev, 100); // 100% allocation

// Read an existing assignment
const assignment = task.getResourceByResourceId(dev.id);
assignment.capacity = 50; // change to 50%

// Remove a resource assignment
task.removeResource(assignment);

Resource Properties

id: stringUnique identifier (read-only).
name: stringDisplay name.
type: ResourceType'unique' (named person) or 'generic' (interchangeable role).
rate: Rate | nullCost rate (value + type: 'perDay' or 'perHour').
fixedCostPerUse: numberOne-time cost each time the resource is used.
fixedCostPerResource: numberFixed cost per resource assignment.
avatarIconUrl: string | nullURL for avatar icon in the UI.

Work & Cost

Each task tracks work effort and cost, calculated from resource assignments:

task.work: Work | nullTotal work effort (e.g. 40h, 5d).
task.cost: number | nullTotal cost computed from resource rates and work.

Constraints

Constraints control how the scheduling engine positions a task relative to a target date.

Constraint Types

asapAs Soon As Possible (default). Task is scheduled at the earliest possible date.
alapAs Late As Possible. Task is scheduled at the latest date without delaying successors.
msoMust Start On. Task must start on the constraint date.
mfoMust Finish On. Task must finish on the constraint date.
snetStart No Earlier Than. Task cannot start before the constraint date.
snltStart No Later Than. Task cannot start after the constraint date.
fnetFinish No Earlier Than. Task cannot finish before the constraint date.
fnltFinish No Later Than. Task cannot finish after the constraint date.
const task = gantt.list.getTaskById('1');
task.constraintType = 'snet';
task.constraintDate = new Date('2027-02-01');

Custom Properties

Add arbitrary fields to tasks for domain-specific data (e.g. priority, department, WBS code). To display custom properties in the sheet, add a custom column.

const task = gantt.list.getTaskById('1');

// Add a custom property
task.addProperty(new CustomProperty('priority', 'High'));

// Read / write
task.getPropertyValue('priority');      // 'High'
task.setPropertyValue('priority', 'Low');

// Look up by name
const prop = task.getPropertyByName('priority');

// Rename or remove
task.renameProperty('priority', 'urgency');
task.removeProperty(prop);