Skip to main content

Gantt

Change over Timechart

Schedule grid — tasks as bars over time, with a task table beside them.

import { Gantt } from "@elabs-ai/components-charts";
Install
npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts

Skipping the @source line is the most common first failure: Tailwind does not scan node_modules, so every component renders unstyled. The one-time wiring

Prompt for your coding agent

Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.

Add the brand-ui Gantt chart (from @elabs-ai/components-charts) to this project.

1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`.
2. Look Gantt up before writing code: `npx -y @elabs-ai/components-cli docs Gantt` prints its real props, variants, what it pairs with and what to avoid.
3. If @elabs-ai/components-charts is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`.
4. Use it here: [the screen or file, and what it should show]
5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop.
6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.

Use it for

  • tasks or phases across a timeline, with dependencies between them

Avoid

  • it is not really scheduled work — a dumbbell chart shows a single before and after
  • Mutating task dates inside the component on drag — Gantt is emit-only; the app owns the model (D5).
  • Bar labels that rely on the bar fill for contrast — check the label against the bar color in every theme.
  • Rendering the grid without status="loading" while rows are still fetching — skeleton rows must hold the layout.

Works with

Used inside

Pairs with

Examples

37 live examples

View Mode Day

View Mode Week

View Mode Month

View Mode Quarter

With Dependencies

With Milestones

Hierarchy Expanded

Hierarchy Collapsed

Virtualized (60 tasks)

Editable

Empty

Loading

Multi Column Grid

Multi-column left grid (P1) — name stays in the accessible Tree; the data columns are an aria-hidden overlay.

Grouped Timescale

Three stacked timescale rows via an explicit `scales` array (P1).

Label Position End

Bar labels placed at the END so short bars/milestones stay legible (P1).

Label Hidden

Bar labels hidden — relies on the task grid / tooltip / aria-label (P1).

Weekend Highlight

Weekend highlight bands via `highlightTime` (P1) — token-backed classes only.

Drag To Edit

Pointer drag (P1, emit-only) — drag a bar body to move, drag the edges to resize; selecting a bar reveals the link handle (drag onto another bar to emit `onDependencyCreate`). The play test performs a real move drag.

Baseline Track

Planned-vs-actual baseline track under each bar (P2).

Gap Bands

Idle-time bands (P2, #221) — a hatched fill spanning a row's `gaps` entries, reusing the ADR 0011 series-pattern mechanism UNCONDITIONALLY (not gated behind high decoration): the meaning ("idle") must not rest on colour alone (WCAG 1.4.1), so the hatch shape is always the second channel. Hover the band, or inspect its `aria-label`, for the accessible name — the caller's `label` when given, else a generated description.

Annotations

Vertical annotation markers (P2) — themed lines + label chips.

Custom Task Types

Custom task types (P2) — color + shape per type.

Inside Label Contrast

Inside-label contrast target (#259) — bars on the lowest-contrast fills (destructive, warning, chart-3) plus a consumer-supplied `taskTypes` color, so the on-bar label pill can be checked across every theme (light/dark) on the fills most likely to fail.

Custom Bar Render

Custom bar renderer (P2) — escape hatch; consumer owns the leaf-bar visual.

Localized

Localized dates (P2) — `locale` drives the default Intl formatting.

Zoomable

Continuous zoom (P2) — Ctrl/⌘ + wheel emits a controlled `cellWidth`.

Sortable Columns

Sortable + resizable columns (P2) — emit-only; the consumer sorts/sizes.

Everything together — grid + grouped timescale + weekend bands + editing.

Agent Run Trace

Sub-day time units (#360) — the same `Gantt` over a **12-second** agent run. `defaultViewMode="auto"` derives the finest readable unit from the data's own span (here: seconds); the toolbar offers `Millisecond / Second / Minute`, and Ctrl/⌘ + scroll zooms continuously. `pixelsPerDay` keeps its exact meaning — pixels per 86 400 000 ms — at every granularity.

Millisecond Trace

Sub-SECOND granularity — a 40 ms dispatch trace with millisecond ticks (`fractionalSecondDigits`). Same component, same `pixelsPerDay` semantics.

Critical Path

The critical path: the zero-float chain gets a solid destructive ring on its bars and solid links (dashed elsewhere). Here Research → Design (c) → Development → Testing → Launch is critical; Prototype (b) has float.

Progress Line

The progress line at a status date (here day 20): bends left on tasks behind plan, right on tasks ahead, stays straight on work not started.

Time Ranges

Highlighted spans behind the bars — a sprint, a freeze, a holiday — with sticky labels.

Rollups

Collapsed phases keep their child milestones and bars as small marks on the summary row.

Zoom Controls

Zoom in / out / fit beside the scale presets. A step keeps the date at the pane centre where it is and morphs every bar, marker and tick to its new place; Ctrl/⌘ + wheel zooms around the pointer. Switching a scale preset is the same animated step.

Scroll To Task

Select a task, zoom until its bar leaves the pane: a round button at the pane edge scrolls it back into view.

API

Gantt

density
comfortable (default)compact
PropTypeDescription
messagesChartMessagesmessages group (RM-187): this chart's own words, keyed by the ui catalogue's `charts.*` message keys. A key set here wins over the `LocaleProvider`; every other key reads the catalogue as before.
tasksrequiredGanttTask[]Task data.
rowHeightnumberRow height override (px). Defaults to density-derived value.
viewModeGanttTimeUnitControlled tick granularity. Accepts any {@link GanttTimeUnit}.
defaultViewModeGanttTimeUnit | "auto"Initial tick granularity. Default `"week"`. Pass `"auto"` to derive the finest readable unit from the data's own span (see {@link pickGanttTimeUnit}) — the default stays `"week"` so no existing surface changes granularity (#360).
onViewModeChange(mode: GanttTimeUnit) => voidEmitted when the granularity changes. **Migration (#360):** the parameter widened from `GanttViewMode` to {@link GanttTimeUnit}. Under `strictFunctionTypes` a handler *explicitly* annotated `(mode: GanttViewMode) => void` no longer assigns — annotate it `GanttTimeUnit`, or drop the annotation (the common inferred form `onViewModeChange={(mode) => …}` is unaffected). Runtime behaviour is unchanged: without `viewModes` the toolbar still only emits the four calendar presets.
viewModesGanttTimeUnit[]Units offered by the toolbar's segmented control. Default `["day", "week", "month", "quarter"]`.
selectedIdstring
defaultSelectedIdstring
onSelect(id: string | undefined) => void
expandedIdsstring[]
defaultExpandedIdsstring[]
onExpandedChange(ids: string[]) => void
onTaskMove(id: string, start: Date, end: Date) => void
onTaskResize(id: string, edge: "start" | "end", date: Date) => void
onDependencyCreate(from: string, to: string) => void
pointerDragbooleanEnable pointer drag (move / resize-edge / create-link), in addition to the always-available keyboard editing. Drag fires the SAME emit-only `onTaskMove` / `onTaskResize` / `onDependencyCreate` — the task model is never mutated (D5). Defaults to `true` when any of those callbacks is provided; pass `false` to force keyboard-only.
columnsGanttColumn[]Left-pane columns. The first column hosts the task name + tree affordances.
scalesGanttScale[]Stacked timescale rows. Overrides the `viewMode`-derived default.
labelPositionGanttLabelPositionBar-label placement. Default `"inside"`.
highlightTimeGanttHighlightTimeReturns a semantic `bg-*` class for a vertical band at `date`, else undefined.
markersGanttMarker[]Vertical annotation markers (themed line + optional label at a date).
timeRangesGanttTimeRange[]Highlighted time spans behind the bars (a sprint, a freeze, a holiday) with a label chip.
showCriticalPathbooleanMark the critical path: the dependency chain with zero float (classic CPM over finish-to-start links, durations = the tasks' spans). Critical bars get a solid `destructive` ring and "critical" in their accessible name; critical links draw solid.
progressLineboolean | Date | string | numberThe project progress line: a vertical line at the status date that bends left to the point each task has reached (behind) or right (ahead). `true` = today.
rollupsbooleanRoll child milestones and bars up onto a collapsed summary row as small marks, so a folded phase still shows what it contains. Default `false`.
renderBar(task: ResolvedTask) => ReactNodeCustom LEAF-bar renderer (escape hatch). Your node fills the bar rect and is `aria-hidden`; the button shell (selection, keyboard editing, pointer drag, tooltip, baseline) is preserved. Summary brackets and milestones keep their built-in rendering. Omit for the default type-driven rendering.
taskTypesRecord<string, GanttTaskType>Custom task types (color/shape) selected per task via `GanttTask.type`.
localestringBCP-47 locale for the default date formatting (passed to `Intl`).
formatDateGanttFormatDateOverride date formatting entirely (P2). Defaults to `Intl` + `locale`.
pixelsPerDaynumberControlled pixels-per-day (continuous zoom). Overrides the view-mode preset — so a scale switch in the toolbar can only change the density through `onPixelsPerDayChange`, which then receives the new scale's preset.
defaultPixelsPerDaynumberInitial pixels-per-day for uncontrolled zoom (enables Ctrl/⌘ + wheel out of the box). A seed for the FIRST view only: picking another scale in the toolbar re-derives the density from that scale's preset (Day / Week / Month / Quarter are zoom levels).
onPixelsPerDayChange(pixelsPerDay: number) => voidEmits the proposed pixels-per-day: on Ctrl/⌘ + wheel zoom, and on a toolbar scale switch (the new scale's preset, floored at "whole domain fits the pane").
zoomBounds{ minPixelsPerDay?: number; maxPixelsPerDay?: number }Override the span-derived zoom clamp (see {@link computeGanttZoomBounds}). Both bounds are in pixels-per-day, at every granularity (#360).
sortGanttSort[]Controlled sort descriptors (drive the header sort indicators).
onSortChange(sort: GanttSort[]) => voidHeader click on a `sortable` column emits the next sort state.
onColumnResize(columnId: string, width: number) => voidHeader-edge drag on a `resizable` column emits the proposed width.
labelColumnWidthnumberLeft pane width (px). Default 240. Ignored when `columns` is set (widths sum).
statusChartStatuschart-state group (RM-194). `"loading"` renders a built-in shimmer loading state (Skeleton rows in both panes) instead of the task tree and canvas, until the tasks are `"ready"`. Use while data is in flight. Default `"ready"`.
emptyChartEmptyStatechart-state group (RM-194): what shows when `tasks` is empty. `message` replaces the default sentence, `title` is printed above it and `action` below it. Unset keys keep the default: no title, the `charts.gantt.noTasksToDisplay` message, no action.
loadingbooleanWhen true, renders a built-in shimmer loading state (Skeleton rows in both panes) instead of the task tree and canvas. Use while data is in flight. @deprecated Since 6.0.0, use `status` — `loading={true}` is `status="loading"`, `loading={false}` is `status="ready"`. Removed in 7.0.0.
childrenReactNode

Also accepts: Omit<HTMLAttributes<HTMLDivElement>, "onSelect">, VariantProps<typeof ganttVariants>

Theming

The tokens each state resolves to. Change the token and every theme follows.

StateToken
statusstatus="loading" → skeleton rows + overlay spinner; "ready" → the grid