mantine-gantt

A gantt chart component for mantine

Installation

yarn add mantine-gantt

Usage

Task Name
Start
End
Duration
Project Planning
Feb 1, 2026
Feb 5, 2026
5d
Requirements Analysis
Feb 3, 2026
Feb 9, 2026
7d
UI/UX Design
Feb 8, 2026
Feb 17, 2026
10d
Database Schema Design
Feb 10, 2026
Feb 14, 2026
5d
API Development
Feb 15, 2026
Feb 28, 2026
14d
Frontend Development
Feb 18, 2026
Mar 7, 2026
18d
Integration Testing
Mar 5, 2026
Mar 11, 2026
7d
User Acceptance Testing
Mar 12, 2026
Mar 16, 2026
5d
Documentation
Feb 20, 2026
Mar 11, 2026
20d
Deployment & Launch
Mar 17, 2026
Mar 19, 2026
3d
January 2026
February 2026
March 2026
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Project Planning
Requirements Analysis
UI/UX Design
Database Schema Design
API Development
Frontend Development
Integration Testing
User Acceptance Testing
Documentation
Deployment & Launch
import { Gantt, type GanttTask } from 'mantine-gantt';

const mockTasks: GanttTask[] =  [
  {
    id: '1',
    label: 'Project Planning',
    startDate: '2026-02-01',
    duration: 5,
    progress: 100,
    dependencies: [],
  },
  {
    id: '2',
    label: 'Requirements Analysis',
    startDate: '2026-02-03',
    duration: 7,
    progress: 80,
    dependencies: ['1'],
  },
  {
    id: '3',
    label: 'UI/UX Design',
    startDate: '2026-02-08',
    duration: 10,
    progress: 60,
    dependencies: ['2'],
    color: 'violet',
  },
  {
    id: '4',
    label: 'Database Schema Design',
    startDate: '2026-02-10',
    duration: 5,
    progress: 40,
    dependencies: ['2'],
    color: 'teal',
  },
  {
    id: '5',
    label: 'API Development',
    startDate: '2026-02-15',
    duration: 14,
    progress: 20,
    dependencies: ['4'],
    color: 'orange',
  },
  {
    id: '6',
    label: 'Frontend Development',
    startDate: '2026-02-18',
    duration: 18,
    progress: 10,
    dependencies: ['3'],
  },
  {
    id: '7',
    label: 'Integration Testing',
    startDate: '2026-03-05',
    duration: 7,
    progress: 0,
    dependencies: ['5', '6'],
    color: 'red',
  },
  {
    id: '8',
    label: 'User Acceptance Testing',
    startDate: '2026-03-12',
    duration: 5,
    progress: 0,
    dependencies: ['7'],
    color: 'pink',
  },
  {
    id: '9',
    label: 'Documentation',
    startDate: '2026-02-20',
    duration: 20,
    progress: 15,
    dependencies: ['2'],
    color: 'gray',
  },
  {
    id: '10',
    label: 'Deployment & Launch',
    startDate: '2026-03-17',
    duration: 3,
    progress: 0,
    dependencies: ['8'],
    color: 'green',
  },
];

function Demo() {
  return <Gantt defaultTasks={mockTasks} />;
}

Custom columns & click-to-open modal

Pass columns to customize the left panel (the default columns are Task Name, Start, End and Duration). Use onTaskClick to react to clicks - here it opens a modal with task details.

Task
Owner
Duration
Project Planning
Ada
5d
UI/UX Design
Grace
10d
API Development
Linus
14d
January 2026
February 2026
March 2026
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
Project Planning
UI/UX Design
API Development
import { useState } from 'react';
import { Gantt, type GanttColumn, type GanttTask } from 'mantine-gantt';
import { Badge, Modal, Stack, Text } from '@mantine/core';

const tasks: Array<GanttTask & { owner: string }> = [
  { id: '1', label: 'Project Planning', startDate: '2026-02-01', duration: 5, progress: 100, owner: 'Ada' },
  { id: '2', label: 'UI/UX Design', startDate: '2026-02-06', duration: 10, progress: 60, owner: 'Grace', dependencies: ['1'], color: 'violet' },
  { id: '3', label: 'API Development', startDate: '2026-02-16', duration: 14, progress: 20, owner: 'Linus', dependencies: ['2'], color: 'orange' },
];

// Custom left-hand columns: Duration is built in, plus an Owner column of our own.
const columns: Array<GanttColumn> = [
  { header: 'Task', render: (t) => t.label },
  { header: 'Owner', render: (t) => (t as any).owner, width: 90 },
  { header: 'Duration', render: (t) => `${t.duration}d`, width: 80 },
];

function Demo() {
  const [selected, setSelected] = useState<GanttTask | null>(null);
  return (
    <>
      <Gantt defaultTasks={tasks} columns={columns} onTaskClick={setSelected} />
      <Modal opened={!!selected} onClose={() => setSelected(null)} title={selected?.label} centered>
        {selected && (
          <Stack gap="xs">
            <Text size="sm">Starts {selected.startDate} · {selected.duration} days</Text>
            <Badge>{selected.progress}% complete</Badge>
          </Stack>
        )}
      </Modal>
    </>
  );
}

Grouping tasks

Set parentId on a task and it becomes a child row. A task that has children renders as a summary bar: it spans its whole subtree and shows a duration-weighted average progress, so the dates you give a parent are only a placeholder. Nesting goes as deep as you need. Rows are collapsible via the chevron - defaultExpandedIds picks which parents start open (omit it and everything is open), and onToggleExpand reports each toggle.

Task Name
Start
End
Duration
Design
Feb 2, 2026
Feb 11, 2026
10d
Wireframes
Feb 2, 2026
Feb 5, 2026
4d
Visual design
Feb 6, 2026
Feb 11, 2026
6d
Build
Feb 12, 2026
Feb 20, 2026
9d
API
Feb 12, 2026
Feb 19, 2026
8d
UI
Feb 16, 2026
Feb 20, 2026
5d
Launch
Feb 26, 2026
Feb 27, 2026
2d
January 2026
February 2026
March 2026
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
Design
Wireframes
Visual design
Build
API
UI
Launch
import { Gantt, type GanttTask } from 'mantine-gantt';

// A task with a parentId becomes a child row; its parent renders as a summary bar
// spanning the whole subtree, with a duration-weighted progress. Nesting is unlimited.
const tasks: GanttTask[] = [
  { id: 'design', label: 'Design', startDate: '2026-02-02', duration: 1, progress: 0 },
  { id: 'wireframes', label: 'Wireframes', startDate: '2026-02-02', duration: 4, progress: 100, parentId: 'design' },
  { id: 'visuals', label: 'Visual design', startDate: '2026-02-06', duration: 6, progress: 60, parentId: 'design', color: 'violet' },
  { id: 'build', label: 'Build', startDate: '2026-02-12', duration: 1, progress: 0 },
  { id: 'api', label: 'API', startDate: '2026-02-12', duration: 8, progress: 40, parentId: 'build', dependencies: ['wireframes'], color: 'orange' },
  { id: 'ui', label: 'UI', startDate: '2026-02-16', duration: 10, progress: 10, parentId: 'build', dependencies: ['visuals'] },
  { id: 'auth', label: 'Auth screens', startDate: '2026-02-16', duration: 5, progress: 0, parentId: 'ui', color: 'teal' },
  { id: 'launch', label: 'Launch', startDate: '2026-02-26', duration: 2, progress: 0, dependencies: ['ui'], color: 'green' },
];

function Demo() {
  return (
    <Gantt
      defaultTasks={tasks}
      // Omit to start with everything expanded. Listed ids stay open, the rest collapse.
      defaultExpandedIds={['design', 'build']}
      onToggleExpand={(id, expanded) => console.log(id, expanded)}
    />
  );
}

View modes

viewMode switches the timeline scale between day (default), week and month. It shrinks the day column derived from columnWidth rather than changing the underlying day grid, so drags still snap to whole days. weekStart controls the header's week numbering and the grid separators.

Task Name
Start
End
Duration
Discovery
Feb 2, 2026
Feb 11, 2026
10d
Implementation
Feb 12, 2026
Mar 7, 2026
24d
Rollout
Mar 9, 2026
Mar 20, 2026
12d
January 2026
February 2026
March 2026
Week 5
Week 6
Week 7
Week 8
Week 9
Week 10
Week 11
Week 12
Week 13
Discovery
Implementation
Rollout
import { useState } from 'react';
import { Gantt, type GanttProps, type GanttTask } from 'mantine-gantt';
import { SegmentedControl, Stack } from '@mantine/core';

const tasks: GanttTask[] = [
  { id: '1', label: 'Discovery', startDate: '2026-02-02', duration: 10, progress: 100 },
  { id: '2', label: 'Implementation', startDate: '2026-02-12', duration: 24, progress: 45, dependencies: ['1'], color: 'orange' },
  { id: '3', label: 'Rollout', startDate: '2026-03-09', duration: 12, progress: 0, dependencies: ['2'], color: 'green' },
];

function Demo() {
  const [viewMode, setViewMode] = useState<GanttProps['viewMode']>('week');

  // 'week' and 'month' shrink the day column (columnWidth / 2 and / 6) so long
  // projects fit without horizontal scrolling. weekStart drives the header numbering.
  return (
    <Stack>
      <SegmentedControl
        value={viewMode}
        onChange={(value) => setViewMode(value as GanttProps['viewMode'])}
        data={['day', 'week', 'month']}
      />
      <Gantt defaultTasks={tasks} viewMode={viewMode} weekStart={1} />
    </Stack>
  );
}

Critical path & baselines

highlightCriticalPath runs CPM over the dependency graph and paints the zero-slack tasks and links with criticalPathColor. Independently, any task with a baseline gets a thin planned bar underneath the actual one - turn those off with showBaselines={false}.

Task Name
Start
End
Duration
Planning
Feb 2, 2026
Feb 5, 2026
4d
Backend
Feb 6, 2026
Feb 13, 2026
8d
Design
Feb 6, 2026
Feb 8, 2026
3d
Integration
Feb 16, 2026
Feb 20, 2026
5d
January 2026
February 2026
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Planning
Backend
Design
Integration
import { Gantt, type GanttTask } from 'mantine-gantt';

// baseline is the planned schedule: it renders as a thin bar under the actual one,
// so slipped tasks are visible at a glance.
const tasks: GanttTask[] = [
  { id: '1', label: 'Planning', startDate: '2026-02-02', duration: 4, progress: 100, baseline: { startDate: '2026-02-02', duration: 3 } },
  { id: '2', label: 'Backend', startDate: '2026-02-06', duration: 8, progress: 60, dependencies: ['1'], baseline: { startDate: '2026-02-05', duration: 7 } },
  { id: '3', label: 'Design', startDate: '2026-02-06', duration: 3, progress: 100, dependencies: ['1'], color: 'teal', baseline: { startDate: '2026-02-05', duration: 3 } },
  { id: '4', label: 'Integration', startDate: '2026-02-16', duration: 5, progress: 0, dependencies: ['2', '3'], baseline: { startDate: '2026-02-12', duration: 5 } },
];

function Demo() {
  // highlightCriticalPath runs CPM over the dependency graph; tasks with zero slack
  // (and the links between them) are painted with criticalPathColor.
  return <Gantt defaultTasks={tasks} highlightCriticalPath criticalPathColor="red" showBaselines />;
}

Milestones

Set type: 'milestone' on a task to render it as a diamond instead of a bar. A milestone is a zero-length marker pinned to its start day - it can be dragged and linked like any task, but it cannot be resized and its duration is ignored for rendering. Milestones participate in the critical path calculation and in auto-scheduling like regular tasks.

Task Name
Start
End
Duration
Requirements
Apr 1, 2026
Apr 5, 2026
5d
Design review
Apr 6, 2026
Apr 8, 2026
3d
Design approved
Apr 9, 2026
Apr 9, 2026
0d
Implementation
Apr 10, 2026
Apr 17, 2026
8d
Go live
Apr 18, 2026
Apr 18, 2026
0d
March 2026
April 2026
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Requirements
Design review
Implementation
import { Gantt, type GanttTask } from 'mantine-gantt';

const tasks: GanttTask[] = [
  {
    id: '1',
    label: 'Requirements',
    startDate: '2026-04-01',
    duration: 5,
    progress: 100,
  },
  {
    id: '2',
    label: 'Design review',
    startDate: '2026-04-06',
    duration: 3,
    progress: 60,
    dependencies: ['1'],
    color: 'violet',
  },
  {
    id: 'm1',
    label: 'Design approved',
    startDate: '2026-04-09',
    duration: 0,
    progress: 0,
    type: 'milestone',
    dependencies: ['2'],
    color: 'grape',
  },
  {
    id: '3',
    label: 'Implementation',
    startDate: '2026-04-10',
    duration: 8,
    progress: 10,
    dependencies: ['m1'],
    color: 'teal',
  },
  {
    id: 'm2',
    label: 'Go live',
    startDate: '2026-04-18',
    duration: 0,
    progress: 0,
    type: 'milestone',
    dependencies: ['3'],
    color: 'red',
  },
];

function Demo() {
  return <Gantt defaultTasks={tasks} showTitle />;
}

Auto-scheduling

With autoSchedule, moving or resizing a task pushes every finish-to-start successor so it never starts before its predecessors end. The cascade is transitive (a whole chain shifts together) and picks the latest predecessor when a task has several dependencies. It works with drags, resizes and keyboard nudges alike. Turned off by default.

Task Name
Start
End
Duration
Foundation
May 4, 2026
May 8, 2026
5d
Framing
May 4, 2026
May 9, 2026
6d
Roofing
May 4, 2026
May 7, 2026
4d
Interior
May 4, 2026
May 11, 2026
8d
April 2026
May 2026
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Foundation
Framing
Roofing
Interior
import { Gantt, type GanttTask } from 'mantine-gantt';

const tasks: GanttTask[] = [
  {
    id: '1',
    label: 'Foundation',
    startDate: '2026-05-04',
    duration: 5,
    progress: 100,
  },
  {
    id: '2',
    label: 'Framing',
    startDate: '2026-05-04',
    duration: 6,
    progress: 40,
    dependencies: ['1'],
    color: 'teal',
  },
  {
    id: '3',
    label: 'Roofing',
    startDate: '2026-05-04',
    duration: 4,
    progress: 0,
    dependencies: ['2'],
    color: 'orange',
  },
  {
    id: '4',
    label: 'Interior',
    startDate: '2026-05-04',
    duration: 8,
    progress: 0,
    dependencies: ['3'],
    color: 'violet',
  },
];

// With autoSchedule, moving or resizing a task pushes every finish-to-start
// successor so it never starts before its predecessor ends.
function Demo() {
  return <Gantt defaultTasks={tasks} autoSchedule showTitle />;
}

Deleting dependency links

Click a dependency arrow to delete that link. The component removes the source id from the target's dependencies and fires onLinkDelete(fromTaskId, toTaskId) alongside onTasksChange, mirroring how link creation works.

Controlled and uncontrolled

Pass defaultTasks and the component owns the task list (uncontrolled) - this is what the demos above do. Pass tasks together with onTasksChange and your own state is the single source of truth (controlled): the component keeps no copy, so a dragged bar only moves once the new tasks prop arrives. Use this mode to drive the chart from a query cache, a store, or an undo/redo stack.

const [tasks, setTasks] = useState(initialTasks);

<Gantt tasks={tasks} onTasksChange={setTasks} />;

onTaskUpdate, onLinkCreate and onLinkDelete keep working in both modes - they fire alongside onTasksChange, not instead of it.

Migrating from 0.2.x: <Gantt tasks={x} /> becomes <Gantt defaultTasks={x} />.