Gantt
A tree-and-timeline chart: a sticky column pane on the left, a scrollable time grid on the right. Groups roll their children up into a summary track, overlapping bars stack into lanes, and the scale switches between hourly and yearly columns.
Bug Tracking
Mobile crash
Release 2.4
July 2026
ARTVLF+1
Bars are coloured by workstream, so a colour always means the same thing. Group rows show a rolled-up span with duration-weighted progress; Ship 2.4 is a milestone.
Gantt
A tree-and-timeline chart with a sticky column pane, group rollups, overlap lanes, milestones and switchable time scales.
Usage
import { Gantt } from "@/components/ui/gantt"
import type { GanttColumn, GanttRow } from "@/components/ui/gantt"
type Task = { status: string; owner: string }
const columns: GanttColumn<Task>[] = [
{ id: "name", header: "Name", width: 230, cell: (row) => row.label },
{ id: "status", header: "Status", width: 110, cell: (row) => row.data?.status },
]
const rows: GanttRow<Task>[] = [
{
id: "epic-1",
label: "Bug Tracking",
children: [
{
id: "task-1",
label: "Login redirect",
data: { status: "Done", owner: "Ana" },
bars: [{
id: "task-1-bar",
start: new Date(2026, 6, 13),
end: new Date(2026, 6, 15),
label: "Login redirect",
color: "blue",
progress: 1,
}],
},
],
},
]
<Gantt<Task>
rows={rows}
columns={columns}
defaultScale="month"
defaultDate={new Date(2026, 6, 22)}
onBarClick={(bar, row) => console.log(bar.id, row.id)}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| rows* | GanttRow<T>[] | — | Row tree. A row with children renders as a collapsible group. |
| columns* | GanttColumn<T>[] | — | Left pane columns. The first one automatically gets the indent and expand/collapse chevron. |
| scale | "day" | "week" | "month" | "quarter" | "year" | — | Controlled time preset. Picks both the visible range and the column unit. |
| defaultScale | GanttScale | "month" | Uncontrolled initial scale. |
| onScaleChange | (scale: GanttScale) => void | — | Fires when the toolbar scale picker changes. |
| scales | GanttScale[] | all five | Presets offered by the toolbar picker. A single-entry array hides it. |
| date | Date | — | Controlled anchor date. The visible range is derived from it. |
| defaultDate | Date | new Date() | Uncontrolled initial anchor date. |
| onDateChange | (date: Date) => void | — | Fires on Today and the prev/next page buttons. |
| now | Date | null | current time | Position of the red now line. Pass null to hide it. Only rendered after mount. |
| weekStartsOn | 0 | 1 | ... | 6 | 0 | First day of the week — affects week columns, week grouping and the week range. |
| title | ReactNode | — | Overrides the derived range label in the toolbar. |
| toolbar | boolean | true | Renders the Today / scale / paging / title bar. |
| actions | ReactNode | — | Rendered at the right end of the toolbar. |
| zoomControls | boolean | true | Floating +/- control that scales column width from 0.5× to 4×. |
| rowHeight | number | 36 | Height of a single-lane row, in px. |
| laneHeight | number | 24 | Extra height each additional overlap lane adds to a row. |
| nonWorking | (start: Date, end: Date) => boolean | weekends | Shades a column as non-working. Defaults to weekends on day columns, nothing otherwise. |
| summaryRows | boolean | true | Group rows render a rollup track spanning their descendants, with duration-weighted progress. |
| bordered | boolean | true | Wraps the chart in a rounded border. |
| height | number | string | 520 | Scroll viewport height. A number is treated as px. |
| onBarClick | (bar: GanttBar, row: GanttRow<T>) => void | — | Makes bars interactive and fires on click. |
| onRowClick | (row: GanttRow<T>) => void | — | Fires when either pane of a row is clicked. |
| onExpandedChange | (expanded: Record<string, boolean>) => void | — | Fires when a group is expanded or collapsed. |
| emptyState | ReactNode | "No rows to show" | Shown when rows is empty. |
| className | string | — | Merged onto the outer wrapper. |
GanttRow<T>
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | — | Unique row key. |
| label* | string | — | Row name, available to every column renderer. |
| bars | GanttBar[] | — | Bars on this row. Overlapping bars are packed into lanes and the row grows taller. |
| children | GanttRow<T>[] | — | Nested rows. Presence of children makes the row a group. |
| defaultExpanded | boolean | true | Start the group collapsed with false. |
| data | T | — | Arbitrary payload handed back to every column's cell renderer. |
GanttBar
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | — | Unique bar key. |
| start* | Date | — | Bar start time. |
| end* | Date | — | Bar end time. Ignored when milestone is true. |
| label | string | — | Placed inside the bar when it fits, otherwise just after it. |
| labelPosition | "auto" | "inside" | "outside" | "none" | "auto" | Overrides the automatic label placement. |
| progress | number | — | 0–1. Fills that fraction of the bar with the saturated hue. |
| color | GanttColor | "neutral" | One of the eight validated identity hues (blue, emerald, violet, amber, rose, teal, orange, indigo) or neutral. |
| variant | "soft" | "solid" | "outline" | "soft" | soft is a tint with a saturated rail; outline is dashed, for planned or queued work. |
| startLabel | string | — | Small muted caption just before the bar's leading edge. |
| endLabel | string | — | Small muted caption after the trailing edge. Skipped when the main label lands outside. |
| icon | ReactNode | — | Rendered before the label, inside the bar. |
| milestone | boolean | false | Renders a ring marker at start instead of a bar. |
| tooltip | ReactNode | — | Replaces the default label / range / progress hover card. |
| disabled | boolean | false | Dims the bar and suppresses onBarClick. |
GanttColumn<T>
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | — | Unique column key. |
| header* | ReactNode | — | Header cell content. |
| width | number | 200 / 120 | Column width in px — 200 for the first column, 120 for the rest. |
| align | "left" | "center" | "right" | "left" | Text alignment. Ignored on the first (tree) column. |
| cell* | (row, ctx) => ReactNode | — | Cell renderer. ctx carries depth, isGroup, expanded and the group's rolled-up span. |