# Gantt timeline

> A roadmap app screen where you drag gantt bars to reschedule, follow dependency arrows past a today line, zoom by day, week or month, and group by phase, team or owner. Selecting a task opens a details panel for owner, dates and progress, and the same plan also shows as a sortable list and an updates feed.

- Type: Screen (a complete app view, installed as a block)
- Page: https://uiarc.dev/components/screens/gantt-timeline
- Markdown: https://uiarc.dev/components/screens/gantt-timeline/markdown

- Access: Arc Pro
- Registry id: `gantt-timeline`
- Source file: `registry/blocks/gantt-timeline/gantt-timeline.tsx`
- Built from: Motion
- Keywords: react gantt chart, roadmap screen, gantt timeline, project timeline, drag to reschedule, task dependencies, group by team, project planning app

Use this as a starting point and replace the sample data with your own.

## When to use

- A product or project roadmap page where tasks span weeks and depend on each other.
- Plans that people reschedule by dragging, with undo, conflict warnings, and per task details.
- Teams that need the same plan as a timeline, a sortable list, and a feed of changes.

## When not to use

- Use kanban-board or project-board for status columns without dates.
- Use week-calendar for time slots within days.
- Use timeline for a read-only list of dated events.

## Installation

Gantt timeline is part of Arc Pro. The live preview is public; the source and install command need Pro.

### CLI with a Pro token

1. Create a token in your account and set it in the environment (or `.env.local`). Never commit it.

```bash
export ARC_PRO_TOKEN=arc_pro_...
```

2. Add the Pro registry to `components.json`:

```json
{
  "registries": {
    "@uiarc": "https://uiarc.dev/r/{name}.json",
    "@uiarc-pro": {
      "url": "https://uiarc.dev/r/pro/{name}.json",
      "headers": {
        "Authorization": "Bearer ${ARC_PRO_TOKEN}"
      }
    }
  }
}
```

3. Install:

```bash
npx shadcn@latest add @uiarc-pro/gantt-timeline
```

### Manual

Signed-in Pro members can copy the source from the Manual tab on the docs page.

- Plans: https://uiarc.dev/pricing
- Create a Pro token: https://uiarc.dev/account#pro-access
- Setup guide: https://uiarc.dev/docs/ai#pro-access

## Usage

```tsx
"use client";

import { useState } from "react";
import { GanttTimeline } from "@/components/arc/blocks/gantt-timeline/gantt-timeline";
import type { GanttGroup, GanttTask, GanttTeam } from "@/components/arc/blocks/gantt-timeline/gantt-timeline";

const groups: GanttGroup[] = [
  { id: "design", name: "Design" },
  { id: "build", name: "Build" },
];

const teams: GanttTeam[] = [
  { id: "web", name: "Web", tone: "sky" },
  { id: "api", name: "API", tone: "teal" },
];

const initial: GanttTask[] = [
  { id: "wires", name: "Wireframes", group: "design", team: "web", start: "2026-11-02", end: "2026-11-06", progress: 1, assignee: "emma-collins" },
  { id: "ui", name: "Visual design", group: "design", team: "web", start: "2026-11-09", end: "2026-11-20", progress: 0.4, dependsOn: ["wires"] },
  { id: "api", name: "API", group: "build", team: "api", start: "2026-11-09", end: "2026-11-27", progress: 0.2, assignee: "daniel-kim" },
  { id: "beta", name: "Beta", group: "build", team: "web", start: "2026-11-30", end: "2026-11-30", milestone: true, dependsOn: ["ui", "api"] },
];

export function RoadmapPage() {
  const [tasks, setTasks] = useState(initial);
  return (
    <div style={{ height: "100dvh" }}>
      <GanttTimeline
        title="Q4 roadmap"
        workspace="Acme"
        groups={groups}
        teams={teams}
        defaultTasks={initial}
        tasks={tasks}
        onTasksChange={(next, change) => {
          setTasks(next);
          if (change.kind === "move" || change.kind === "resize") {
            void fetch("/api/tasks/" + change.id, { method: "PATCH", body: JSON.stringify({ start: change.start, end: change.end }) });
          }
        }}
        today="2026-11-12"
        defaultCollapsed={[]}
        updates={[]}
      />
    </div>
  );
}
```

## API reference

### GanttTimeline

A full roadmap screen inside the Arc app shell. The timeline groups tasks by phase, team, or owner, with a today line, dependency arrows that re-route as bars move and turn red on a conflict, and zoom that glides between day, week, and month. Drag a bar to move it or its edges to change dates. Selecting a task opens the details panel on demand to edit owner, team, phase, dates, and progress; the header's panel button opens the project overview. The sidebar lists the plan's milestones (selecting one opens it on the timeline) and saved views; the toolbar filters by name and by owner in a popover. A list view and an updates feed read the same plan. Fills its container. Also the default export.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` | `"Atlas 4.0"` | Project name in the breadcrumbs and the overview panel. |
| `groups` | `GanttGroup[]` | `GROUPS` | Phases in display order. Each task's group field refers to a group id. |
| `teams` | `GanttTeam[]` | `TEAMS` | Teams with an Arc tone and an optional icon. The tone colours the bars and tints the icon, which marks the team in lanes, the list, and the team picker. They also power grouping by team. |
| `tasks` | `GanttTask[]` | – | Controlled tasks. Pair with onTasksChange. |
| `defaultTasks` | `GanttTask[]` | `TASKS` | Starting tasks when uncontrolled. Also sets the calendar range and the owner filter once on mount, and is what Reset restores. |
| `onTasksChange` | `(tasks: GanttTask[], change: GanttChange) => void` | – | Called after a move, resize, edit, add, delete, undo, or reset with the full list and what changed. |
| `today` | `string` | `"2026-11-12"` | ISO date that places the today line, the initial scroll, and every status. |
| `zoom` | `GanttZoom` | – | Controlled zoom: "day", "week", or "month". |
| `defaultZoom` | `GanttZoom` | `"week"` | Starting zoom when uncontrolled. |
| `onZoomChange` | `(zoom: GanttZoom) => void` | – | Called when the zoom control changes. |
| `collapsed` | `string[]` | – | Controlled ids of folded lanes. Phase lanes use the group id, team lanes "team:<id>", and owner lanes "owner:<person id>". |
| `defaultCollapsed` | `string[]` | `["discovery"]` | Lanes folded on first render when uncontrolled. |
| `onCollapsedChange` | `(ids: string[]) => void` | – | Called when a lane is folded or unfolded. |
| `defaultGrouping` | `GanttGrouping` | `"phase"` | How rows are grouped on first render: "phase", "team", or "owner". |
| `updates` | `GanttUpdate[]` | `UPDATES` | Seed entries for the updates feed, newest first. Your own changes are added on top. |
| `workspace` | `string` | `"Waypoint"` | Workspace name in the sidebar switcher (drawn with its own ScreenShellMark logo) and the breadcrumbs. |
| `className` | `string` | – | Extra class on the root. |

### GanttTask

Exported type. { id: string; name: string; group: string; start: string; end: string; progress?: number; assignee?: PersonId; dependsOn?: string[]; milestone?: boolean; team?: string; links?: GanttLink[] }. Dates are ISO days and end is inclusive; a milestone uses start only. progress runs from 0 to 1.

No props.

### GanttTeam

Exported type. { id: string; name: string; tone: GanttTone; icon?: ComponentType<{ size?; strokeWidth?; className? }> }, where tone is one of Arc's categorical tones (teal, sky, orchid, violet, rose, amber, green) and icon is a lucide icon such as PenTool or Server. Without an icon a team shows a people glyph.

No props.

### GanttLink

Exported type. { kind: "github" | "figma" | "stripe" | "apple" | "google"; label: string; detail?: string; url: string }. Shown with its brand mark in the details panel; selecting one copies the URL.

No props.

### GanttGroup

Exported type. { id: string; name: string }.

No props.

### GanttUpdate

Exported type. { id: string; person?: PersonId; text: string; task?: string; at: string }. Leave person out for the signed in person.

No props.

### GanttChange

Exported type passed to onTasksChange. { id: string; start: string; end: string; kind: "move" | "resize" | "undo" | "reset" | "edit" | "add" | "delete" }.

No props.

## Keyboard interactions

| Keys | Action |
| --- | --- |
| Tab | Moves through the sidebar, the toolbar, and into the chart, where one bar is tabbable at a time. |
| Arrow Down / Arrow Up | In the chart, moves focus to the next or previous visible bar and scrolls it into view. In the list view, moves between rows. |
| Alt + Arrow Left / Right | Moves the focused task by one day. |
| Alt + Shift + Arrow Left / Right | Changes the focused task's end date by one day. |
| Enter / Space | Opens the focused bar in the details panel; on narrow frames the panel opens as a sheet. |
| Escape | Returns the details panel to the overview, closes an open list, or clears the filter text. |
| / | Focuses the task filter. |
| Arrow Left / Right on the zoom control | Moves between day, week, and month. |
| ⌘K / Ctrl+K | Focuses the sidebar search (from the app shell). |

## Accessibility

- Bars are role="button" with aria-roledescription "Task bar" or "Milestone", aria-pressed for selection, and a label with name, dates, percent done, and a warning when a dependency is violated. Hidden text describes the Alt shortcuts.
- Folded and filtered rows are aria-hidden and inert; lane toggles carry aria-expanded.
- Zoom is Arc's SegmentedControl (aria-pressed buttons with arrow keys) and grouping is Arc's Select. The owner filter is a Popover of labelled checkboxes, and the owner, team, and phase fields are listboxes inside a Popover with aria-activedescendant, Escape, and focus returned to the trigger.
- Sidebar milestones are buttons with aria-pressed for the selected one and a label that includes the date and whether the milestone is reached or in conflict.
- The footer status is role="status" and reports each change and conflict; other views keep a hidden live region.
- The list view is a real table with aria-sort on the sortable headers and one roving tab stop.
- Status is never colour alone: every status icon has a different shape and a text label in the badge and the list; milestones are diamonds and teams are icons, never coloured dots.

## Motion

- Bars, arrows, lane summaries, and task rows read the same motion values, so they move together without drifting.
- After a drag, the bar springs to the snapped day from its release velocity; a dashed snap target and a date label show while dragging.
- Zoom animates the day scale on the smooth spring while keeping the middle of the view fixed; the header tiers crossfade.
- Changing the grouping glides each task to its new row while the new lane labels fade in; folding a lane slides its rows into the lane row.
- The details panel opens on the shell's spring and crossfades between the overview and a task; the view switch is one fade; summary numbers roll with AnimatedCounter and the done bar is Arc's Progress.
- Selection fills in place: a selected row, list row, or sidebar milestone gets a calm fill with no travelling highlight. Hover fills are instant.
- Reduced motion jumps bars, rows, scale, and scroll to their targets and removes the fades and presses.

## Responsive behavior

- From 1080px the details panel sits beside the timeline; below that it is a sheet opened from the header or by selecting a task.
- Below 900px the sidebar becomes a sheet; below 600px the page goes edge to edge, header actions become icons, and toolbar labels drop to icons.
- When the chart is under 560px wide the sticky task column narrows to 136px and hides avatars and dates.
- The list view drops the owner column at 900px, the start column at 760px, and progress at 600px. The legend hides below 780px.
- Scrollbars are hidden everywhere; edge fades show where the chart, list, panel, or toolbar continues.

## Performance

- Drag, zoom, regrouping, and folding update motion values; React re-renders on commit and when the snapped day changes.
- Each dependency arrow recomputes its path from motion values every frame while something moves; keep plans in the tens of tasks.
- The header and grid lines render one element per day at day zoom, so very long ranges add DOM weight.

## Notes for AI

- Use as a whole roadmap page: it renders the app shell (sidebar, header, details panel) itself, so give it a parent with a definite height.
- The calendar range is computed once on mount from defaultTasks and today, starting on a Monday with a week before and four weeks after. In controlled mode pass defaultTasks as well.
- Statuses are derived: done at 100%, conflict when a task starts before one it depends on ends, behind when progress trails the elapsed share by more than 15 points, and not started before the start date.
- onTasksChange reports every edit; for moves and resizes persist change.id with the ISO dates. Undo keeps the last 30 changes, and slider drags fold into one step.
- Owners are PersonId values from @/components/arc/lib/media and render as seeded gradient avatars. Brand marks in links are sample content.
- Dependency conflicts are shown, not prevented. Validate on the server if a violation must be blocked.

## Related

- [Project board](https://uiarc.dev/components/screens/project-board/markdown): A full project overview screen: live progress stats, a milestone track that picks the task list, recent activity and a team panel. Tasks group by stage with filters and drag to reorder, and the task panel edits fields, subtasks and comments in place.
- [Kanban board](https://uiarc.dev/components/screens/kanban-board/markdown): A complete project board inside the Arc app shell, with projects, saved views and live cycle progress in the sidebar, filters for priority, labels and assignee, and cards you can drag between columns or swimlanes. A details panel slides over the board to edit status, priority, assignee, labels, estimate and due date, with sub-issues, linked pull requests, activity and comments.
- [Week calendar](https://uiarc.dev/components/screens/week-calendar/markdown): A full calendar app with a week grid. Drag to create, move and resize events, answer invites right on the grid, and overlay teammates' calendars from a side panel with a mini month and the next meeting. Pages for invites and booking links complete the app.
- [Timeline](https://uiarc.dev/components/timeline/markdown): Follow what happened, newest first, grouped by day.

## Guidance for AI tools

Blocks are complete, self-contained screens with sample data. Replace the sample data and connect the callbacks described above. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://uiarc.dev/llms.txt
