@jaeungkim/gantt-chart

Task and task types

`Task`, `TaskDependency`, `DependencyType`, `TaskType`, `TaskTransformed`

Task is the shape of one row of the tasks prop, and TaskTransformed is what the chart hands back to a renderer after it has measured that row. Both, along with TaskDependency, DependencyType and TaskType, are type-only exports of the package root.

import type {
  DependencyType,
  Task,
  TaskDependency,
  TaskTransformed,
  TaskType,
} from '@jaeungkim/gantt-chart';

Behaviour lives in the guides: Task data for dates and sequence, Editing tasks for the interaction flags, Scheduling for manuallyScheduled and the baselines.

TaskType and DependencyType

export type TaskType = 'task' | 'milestone';

export type DependencyType = 'FS' | 'SS' | 'FF' | 'SF';
ValueFirst letterSecond letter
'FS'predecessor's finishsuccessor's start
'SS'predecessor's startsuccessor's start
'FF'predecessor's finishsuccessor's finish
'SF'predecessor's startsuccessor's finish

TaskDependency

A dependency is stored on the successor, and targetId names the predecessor.

export interface TaskDependency {
  /** The predecessor's id - a task's `dependencies` list the tasks it waits on */
  targetId: string;
  type: DependencyType;
  /**
   * Signed delay between the two ends of the link, in days
   *
   * Positive is lag (wait this long after the predecessor), negative is lead (overlap).
   * Counted in working days when the working-day calendar is on, calendar days otherwise.
   */
  lag?: number;
}
FieldTypeRequiredMeaningRead when
targetIdstringyesId of the predecessor. An id that is not in tasks is dropped silently — no arrow, no scheduling edge. An id equal to the owner's own is dropped by the scheduling graph only; the arrow layer has no self check, so a self-link still draws an arrow back onto the same row.always
typeDependencyTypeyesWhich end of each task the link anchors to. A value outside the four is skipped; the console.warn fires once per distinct value and only in a DEV build.always
lagnumbernoSigned days, default 0. Calendar days, or working days with workingCalendar on.schedulingPolicy is 'shift-on-overlap' or 'maintain-gap', or criticalPath is on

The four link types and how arrows are drawn and deleted: Dependencies.

Task

export interface Task {
  id: string;
  name: string;
  startDate: string;
  endDate: string;
  parentId: string | null;
  sequence: string;
  /** Task kind - 'milestone' renders as a diamond at startDate (default 'task') */
  type?: TaskType;
  /** Progress 0-100 (%) - omitted means no progress display */
  progress?: number;
  /**
   * Bar color - any CSS color value
   *
   * The progress fill and the hover shade are derived from it, so one value colors the
   * whole bar. Omitted, the `--gantt-*` theme tokens decide as before.
   */
  color?: string;
  /** Extra class name put on this task's bar and its task-list row */
  className?: string;
  /**
   * Swimlane this task shares a row with
   *
   * Tasks with the same lane (inside the same group) are drawn side by side on one
   * row; overlapping ones stack onto extra rows automatically. Omitted, the task
   * gets a row of its own as before.
   */
  lane?: string;
  dependencies?: TaskDependency[];
  /** Blocks every gesture on this task - overrides the chart's `readOnly` prop */
  readOnly?: boolean;
  /** Allows/blocks moving this task - overrides both `readOnly` settings */
  allowMove?: boolean;
  /** Allows/blocks resizing this task - overrides both `readOnly` settings */
  allowResize?: boolean;
  /** Allows/blocks dragging this task's progress handle - overrides both `readOnly` settings */
  allowProgressChange?: boolean;
  /** Allows/blocks starting a dependency drag from this task - overrides both `readOnly` settings */
  allowLinkCreate?: boolean;
  /** Allows/blocks deleting a dependency this task owns - overrides both `readOnly` settings */
  allowLinkDelete?: boolean;
  /** Earliest date this task may be dragged to (ISO string) - overrides the chart's `minDate` */
  minDate?: string;
  /** Latest date this task may be dragged to (ISO string) - overrides the chart's `maxDate` */
  maxDate?: string;
  /** The scheduling engine never moves this task; it still constrains its successors */
  manuallyScheduled?: boolean;
  /** Planned start snapshot - drawn as a thin bar under the live one (UTC ISO string) */
  baselineStart?: string;
  /** Planned end snapshot (UTC ISO string) */
  baselineEnd?: string;
}

Six fields are required: id, name, startDate, endDate, parentId, sequence.

FieldTypeRequiredMeaningRead when
idstringyesUnique key for links, roll-up and undo patches. Uniqueness is not checked; maps keyed by id keep the last occurrence.always
namestringyesBar label, task-list cell and ARIA label.always
startDatestringyesAny string dayjs.utc() parses. A bare '2025-06-01' is UTC midnight; a zone-less '2025-06-01T09:00' is a UTC wall clock.always
endDatestringyesSame parsing. Ignored for a milestone in rendering, roll-up, lane packing and scheduling — but still counted by the timeline range and by a marker's warnOnOverrun.always
parentIdstring | nullyesId of the parent, or null for a root. An orphan, a self-reference or a cycle is cut and the task becomes a root.hierarchy is on
sequencestringyesDot-separated numbers, e.g. '2.10'. Decides row order; segments compare numerically, a missing segment is 0, a non-numeric segment is 0. Supplies depth when hierarchy is off.always
typeTaskTypenoDefault 'task'. Only the exact string 'milestone' makes a milestone.always
progressnumberno0–100 (%). Clamped, never rejected: -100, 150100, Infinity100. A non-number or NaN means no progress display. No rounding.always
colorstringnoAny CSS color. Sets --gantt-bar-color, plus a hover shade at 86% and a progress fill at 62% mixed with #000. An empty or whitespace-only string is ignored.always
classNamestringnoAdded to both the bar and the task-list row.always
lanestringnoTasks sharing a lane inside a group pack onto one row; overlapping ones stack onto extra rows. Absent or empty means a row of its own.always
dependenciesTaskDependency[]noPredecessors of this task. Default [].always
readOnlybooleannoBlocks every gesture on this task.always
allowMovebooleannoOverrides readOnly on both levels for the move gesture.always
allowResizebooleannoSame, for resizing. Forced false on a milestone and on a summary row.always
allowProgressChangebooleannoSame, for the progress handle. Forced false on a summary row.always
allowLinkCreatebooleannoSame, for starting a dependency drag.always
allowLinkDeletebooleannoSame, for deleting a dependency this task owns.always
minDatestringnoEarliest drag target, UTC ISO. Falls back to the chart's minDate; the capability chain does not apply.always
maxDatestringnoLatest drag target, UTC ISO. Same fallback.always
manuallyScheduledbooleannoThe scheduling engine never moves this task; it still constrains its successors. It does not stop a user dragging the bar.schedulingPolicy is 'shift-on-overlap' or 'maintain-gap'
baselineStartstringnoPlanned start, UTC ISO. Its presence alone draws the baseline bar and widens the timeline range.always
baselineEndstringnoPlanned end, UTC ISO. Without baselineStart it is ignored; with baselineStart and no baselineEnd the baseline is a single point.always

Task-level flags beat readOnly, which beats the chart-level equivalents: see GanttInteractionConfig for the full resolution order.

TaskTransformed

Every added field is computed by the chart and read-only. TaskTransformed is output-only — tasks is always Task[], and a mutated TaskTransformed is never read back.

export interface TaskTransformed extends Task {
  barLeft: number;
  barWidth: number;
  depth: number;
  order: number;
  originalOrder: number;
  /**
   * A summary row with children (true only when hierarchy is on)
   *
   * Its start/end are recomputed from the children, so resizing and progress editing are
   * disabled and dragging the bar moves the whole subtree.
   */
  isSummary?: boolean;
  dependencies?: TaskDependency[];
  /** Baseline bar geometry - present only when the task carries baseline dates */
  baselineLeft?: number;
  baselineWidth?: number;
  /** CPM outputs - present only while the `criticalPath` prop is on (read-only) */
  earlyStart?: string;
  earlyFinish?: string;
  lateStart?: string;
  lateFinish?: string;
  totalSlack?: number;
  freeSlack?: number;
  critical?: boolean;
  /** Duration in calendar days, or working days when the working-day calendar is on */
  duration?: number;
}
FieldTypeComputed valuePresent when
every Task fieldcopied through unchangedalways
barLeftnumberpx from the timeline origin; 0 when the timeline has no cellsalways
barWidthnumberpx, clamped to a minimum of 1; 0 only when the timeline has no cellsalways
depthnumberparentId chain depth with hierarchy on, dot count in sequence otherwisealways
ordernumber1-based row number after grouping and lane packingalways
originalOrdernumberthe 1-based index in the sequence-sorted array, before the order rewritealways
isSummarybooleantrue when the task has children. Always false with hierarchy off. Typed optional, written on every task.always
baselineLeftnumberpx from the timeline origin for the baseline barkey always written; value undefined without baselineStart
baselineWidthnumberpx width of the baseline barsame
earlyStartstringUTC ISOcriticalPath is on
earlyFinishstringUTC ISOcriticalPath is on
lateStartstringUTC ISOcriticalPath is on
lateFinishstringUTC ISOcriticalPath is on
totalSlacknumberlateShift - earlyShift, in days — working days with workingCalendar oncriticalPath is on
freeSlacknumbersmallest float across the outgoing links, floored at 0; equal to totalSlack with no successorscriticalPath is on
criticalbooleantotalSlack === 0 and progress is not 100criticalPath is on
durationnumberdays between start and end — calendar days, or working days with workingCalendar on. A milestone's is 0.criticalPath is on

A task caught in a dependency cycle is excluded from the CPM order, so its eight CPM fields stay absent even with criticalPath on. Slack and criticality: Scheduling and computeCriticalPath.

Notes

isMilestoneTask and normalizeProgress are not exported from the package root. They exist in the source and are re-exported by src/types/task.ts, but src/index.ts does not list them, so import { isMilestoneTask } from '@jaeungkim/gantt-chart' fails. Write task.type === 'milestone' and Math.min(100, Math.max(0, progress)) in the host app instead. RenderedDependency is not exported either.

Task gets no runtime validation: no required-field check, no date parsing check, no id-uniqueness check, no progress range check on input. The one runtime diagnostic in the whole model is the DEV-only warning for an unrecognized DependencyType.

sequence is the only field whose absence throws. sequence.split('.') runs during layout, so a hand-built task without it crashes the chart rather than degrading.

Dates go in as anything dayjs.utc() parses and come back out as Dayjs.toISOString()YYYY-MM-DDTHH:mm:ss.sssZ. There is no local time-zone mode.

Related pages: GanttProps, GanttInteractionConfig, Task data.

On this page