Skip to content

Task list

The list of tasks that a classic gantt tool has on its left: a row for every row of the chart, at the same height and place, indented by depth, with a toggle on the groups, the columns you choose and a splitter on its edge. It is createTaskList for @ganttlane/dom and <TaskList> for @ganttlane/react. The guide has all of it; the task list beside the chart example draws one by hand from the same public API, for when you want your own markup.

Terminal window
pnpm add @ganttlane/dom # vanilla
pnpm add @ganttlane/react react react-dom # React
View on GitHub
data.ts
import type { Link, Task } from '@ganttlane/core';
// A plan of thirty tasks in four groups (Plan, Build, Test, Launch), nested two levels deep: a
// group holds tasks and sub-groups, a sub-group holds tasks. `parentId` makes the tree, and the
// rows are the tasks in pre-order (a group, then what is in it). That is more rows than the demo
// shows at once, so the list scrolls. 2026-03-02 is a Monday; a task with an `end` ends on that
// day (inclusive).
//
// A group has dates of its own that cover its children, and `followParent` ties a child to its
// group: drag a group and the children follow, and the list shows their new dates.
export const tasks: Task[] = [
// ---- Plan ----
{ id: 'plan', name: 'Plan', start: '2026-03-02', end: '2026-03-20', progress: 100 },
{
id: 'research',
name: 'Research',
parentId: 'plan',
start: '2026-03-02',
end: '2026-03-06',
progress: 100,
followParent: true,
},
{
id: 'scope',
name: 'Scope',
parentId: 'plan',
start: '2026-03-09',
end: '2026-03-10',
progress: 100,
followParent: true,
},
{
id: 'architecture',
name: 'Architecture',
parentId: 'plan',
start: '2026-03-11',
end: '2026-03-20',
progress: 100,
followParent: true,
},
{
id: 'system-design',
name: 'System design',
parentId: 'architecture',
start: '2026-03-11',
end: '2026-03-17',
progress: 100,
followParent: true,
},
{
id: 'data-model',
name: 'Data model',
parentId: 'architecture',
start: '2026-03-16',
end: '2026-03-19',
progress: 100,
followParent: true,
},
{
id: 'api-contract',
name: 'API contract',
parentId: 'architecture',
start: '2026-03-18',
end: '2026-03-20',
progress: 100,
followParent: true,
},
// ---- Build ----
{ id: 'build', name: 'Build', start: '2026-03-23', end: '2026-05-01', progress: 45 },
{
id: 'backend',
name: 'Backend',
parentId: 'build',
start: '2026-03-23',
end: '2026-04-17',
progress: 80,
followParent: true,
},
{
id: 'data-layer',
name: 'Data layer',
parentId: 'backend',
start: '2026-03-23',
end: '2026-04-03',
progress: 100,
followParent: true,
},
{
id: 'services',
name: 'Services',
parentId: 'backend',
start: '2026-04-06',
end: '2026-04-14',
progress: 75,
followParent: true,
},
{
id: 'endpoints',
name: 'Endpoints',
parentId: 'backend',
start: '2026-04-15',
end: '2026-04-17',
progress: 40,
followParent: true,
},
{
id: 'frontend',
name: 'Frontend',
parentId: 'build',
start: '2026-03-30',
end: '2026-04-24',
progress: 50,
followParent: true,
},
{
id: 'components',
name: 'Components',
parentId: 'frontend',
start: '2026-03-30',
end: '2026-04-10',
progress: 100,
followParent: true,
},
{
id: 'screens',
name: 'Screens',
parentId: 'frontend',
start: '2026-04-08',
end: '2026-04-22',
progress: 50,
followParent: true,
},
{
id: 'state-handling',
name: 'State handling',
parentId: 'frontend',
start: '2026-04-15',
end: '2026-04-24',
progress: 10,
followParent: true,
},
{
id: 'integration',
name: 'Integration',
parentId: 'build',
start: '2026-04-20',
end: '2026-05-01',
progress: 0,
followParent: true,
},
// ---- Test ----
{ id: 'test', name: 'Test', start: '2026-04-27', end: '2026-05-15' },
{
id: 'unit-tests',
name: 'Unit tests',
parentId: 'test',
start: '2026-04-27',
end: '2026-05-06',
followParent: true,
},
{
id: 'end-to-end-tests',
name: 'End-to-end tests',
parentId: 'test',
start: '2026-05-04',
end: '2026-05-12',
followParent: true,
},
{
id: 'performance',
name: 'Performance',
parentId: 'test',
start: '2026-05-06',
end: '2026-05-15',
followParent: true,
},
{
id: 'load-test',
name: 'Load test',
parentId: 'performance',
start: '2026-05-06',
end: '2026-05-12',
followParent: true,
},
{
id: 'profiling',
name: 'Profiling',
parentId: 'performance',
start: '2026-05-11',
end: '2026-05-15',
followParent: true,
},
{
id: 'bug-fixes',
name: 'Bug fixes',
parentId: 'test',
start: '2026-05-11',
end: '2026-05-15',
followParent: true,
},
// ---- Launch ----
{ id: 'launch', name: 'Launch', start: '2026-05-18', end: '2026-05-29' },
{
id: 'docs',
name: 'Docs',
parentId: 'launch',
start: '2026-05-18',
end: '2026-05-22',
followParent: true,
},
{
id: 'guides',
name: 'Guides',
parentId: 'docs',
start: '2026-05-18',
end: '2026-05-21',
followParent: true,
},
{
id: 'api-reference',
name: 'API reference',
parentId: 'docs',
start: '2026-05-19',
end: '2026-05-22',
followParent: true,
},
{
id: 'release-candidate',
name: 'Release candidate',
parentId: 'launch',
start: '2026-05-25',
end: '2026-05-27',
followParent: true,
},
{
id: 'release',
name: 'Release',
type: 'milestone',
parentId: 'launch',
start: '2026-05-29',
followParent: true,
},
];
export const links: Link[] = [
{ source: 'research', target: 'scope' },
{ source: 'scope', target: 'system-design' },
{ source: 'api-contract', target: 'data-layer' },
{ source: 'data-layer', target: 'services' },
{ source: 'services', target: 'endpoints' },
{ source: 'endpoints', target: 'integration' },
{ source: 'integration', target: 'end-to-end-tests' },
{ source: 'bug-fixes', target: 'release-candidate' },
{ source: 'release-candidate', target: 'release' },
];

Files

  • data.ts: thirty tasks in four groups, nested two levels deep, with a few links.
  • options.ts: the sizes, the stripes, the view, and selection so that a click on a bar selects it.
  • helpers.ts: the columns, and the text of the Dates column.
  • styles.css: the two side by side, and the box of layout.height: 'auto'.
  • vanilla.ts: createGantt, createTaskList and the toolbar.
  • react.tsx: the same with useGantt and <TaskList>.

Options

Option Value What it does
columns [{ id: 'name', … }, { id: 'dates', … }] The columns of the list; the first is the tree column, with the indent and the toggle
selection.onClick true A click on a bar selects its task, and the list shows it
selection.clearOnEmptyClick true A click on an empty part of the chart clears the selection
layout.height 400, or 'auto' with the Height select Who scrolls the rows: the chart, or the box around the list and the chart
layout.rowHeight, headerRowHeight 36, 28 The sizes the list reads back, so that its rows line up
display.rowStripes 'group' Zebra stripes for each group, the same in the list
theme 'auto' The Theme select; the list follows the chart

What to try

  • Scroll. Wheel over the names or over the chart: the other follows.
  • Click a name far down the list (Release): the chart scrolls sideways to its bar and selects it, and nothing moves if the bar is already in view. Click a bar: its name is selected. Click empty space in the chart: the selection is cleared.
  • Open and shut. Click the chevron of Build, or press Left with the focus on it.
  • Drag a bar: its dates change when you drop it, and so do the dates of what it pushed.
  • Point at a row, in the list or in the chart: both light up.
  • Drag the splitter on the edge of the list, or focus it and press Left and Right.
  • Use the keyboard. Tab to the list, then Up, Down, Page Up, Page Down, Home, End, Left and Right. Enter selects the row and scrolls the chart to its bar.
  • Change the Height. With fixed the chart scrolls and the list follows it, which during a fast fling can be a frame behind. With auto one box scrolls both, and they cannot differ.

Notes

  • The row hover is on here because a list is attached. A chart without one lights no row, unless you set display.rowHover or --gantt-row-hover-color yourself.
  • A list draws what the chart draws, the rows in view and a few more, so it stays fast with thousands of tasks.
  • Dates follow a commit, not the drag in progress: the record of a task changes when the change is committed.