Gantt
Plan projects with a Material timeline, task progress, and milestones.
Turn a schedule into a shared timeline. Gantt brings rounded task bars, progress indicators, and milestone diamonds to your Material theme, with day and week views for projects of different sizes.
Plan a website launch
Follow overlapping work from discovery to release. With Edit tasks enabled, drag a bar to move it or drag either end handle to shorten or extend it. Use zoom to choose how much of the schedule you see. The vertical line marks today; shaded columns mark weekends.
Select a task to inspect it or update its progress.
Default progress colors: blue below 30%, green from 30% to below 90%, orange from 90% through 100%, and red above 100%. Discovery uses 115% of its expected time. Click a task bar, then press Progress +10% to explore the transitions, including overruns. Configure progress colors.
Use the checkboxes beside task names, Ctrl/Cmd-click task bars to toggle them, or Shift-click another checkbox or task bar to select every task between the two, in either direction. Move any selected bar to move the whole group. Drag either end handle to trim that end of all selected tasks; milestones keep their single date during trimming.
Scroll inside the timeline to explore the schedule. Task labels stay visible. Tab to a task and press Enter or Space to select it. In edit mode, Left/Right arrows move the task by one day; Shift + Left/Right changes its end date. Tab to either resize handle and use Left/Right to change that end. Escape cancels a drag. Examples use dates relative to today and changes stay in this page.
Dependencies with overlapping work
Links describe which tasks follow a move. They allow any overlap: Implementation has already started while Planning is still scheduled, and Testing overlaps Implementation. Move Planning to try the chain; Marketing is independent.
Connect directly: hover over a task to reveal its round + handle, styled like the organization chart, then drag it onto a downstream task. You can also click or tap the handle, then the destination bar. With a keyboard, activate the handle with Enter or Space, Tab to the destination, and activate it. Escape cancels. Disconnect: hover over a dependency curve and click its × button, or use the button beside the dependency name in the task’s left-hand label. Controls appear on keyboard focus and stay visible on touch screens. These controls never change scheduled dates.
Move Planning later: Implementation keeps its start and extends its end; Testing and Release move. Move Planning earlier: Implementation stays unchanged.
A task lists its upstream IDs in dependencies. Moving it affects all downstream tasks, including indirect links, once each. Upstream tasks and unrelated tasks stay unchanged. Selecting multiple linked tasks does not apply the same move twice.
- Unstarted downstream task (0%): start and end move by the same number of days as the task you moved.
- Started downstream task (above 0%, including overruns): moving its predecessor later extends only its end date. Moving the predecessor earlier leaves both dates unchanged.
- Resizing: affects the selected tasks only. Dependencies do not propagate trimming.
const chart = Gantt.init(document.querySelector<HTMLElement>('#schedule')!, {
editable: true,
connectable: true, // Direct dependency controls (enabled by default in edit mode)
tasks: [
{ id: 'planning', name: 'Planning', start: '2026-10-01', end: '2026-10-07' },
{ id: 'build', name: 'Implementation', start: '2026-10-03', end: '2026-10-12',
progress: 40, dependencies: ['planning'] },
{ id: 'qa', name: 'Testing', start: '2026-10-10', end: '2026-10-15',
dependencies: ['build'] }
],
onTaskChange: change => console.log(change),
onDependencyChange: change => console.log(change)
});
chart.addDependency('planning', 'qa'); // Upstream → downstream; dates stay unchanged
chart.removeDependency('planning', 'qa');
chart.getTasks(); // Includes independent copies of dependency arraysOnly pointer and keyboard moves propagate dependencies. Adding a link, initializing data, or calling setTasks() preserves the dates you supply. Unknown IDs, self-links, duplicate IDs in a dependency array, and cycles are rejected without changing the chart. Re-adding an existing link or removing an absent link is a no-op.
A task selected together with one of its upstream tasks is still treated as downstream: if it has started, its start stays fixed. Unstarted milestones move with the chain; started milestones stay at their original date because a milestone has no duration. The move continues to their downstream tasks. Progress values remain unchanged.
All affected dates commit together. onTaskChange runs once per changed task: action is 'move' for moved tasks and 'resize-end' for started successors whose end was extended. Escape or a canceled pointer gesture restores the entire chain. onDependencyChange receives { from, to, action: 'add' | 'remove' }; import GanttDependencyChange for its type.
Coordinate a campaign
Use view: 'week' for a longer schedule. Each header interval begins on Monday, while task positions still reflect their exact calendar dates. Theme colors distinguish the teams and a diamond marks final approval.
Select a task or milestone to see its dates.
Gantt.init(document.querySelector<HTMLElement>('#campaign')!, {
view: 'week',
label: 'Campaign schedule',
tasks: [
{ id: 'creative', name: 'Creative development', detail: 'Studio',
start: '2026-10-05', end: '2026-10-23', progress: 40, tone: 'secondary' },
{ id: 'content', name: 'Content production', detail: 'Editorial',
start: '2026-10-12', end: '2026-10-30', progress: 10, tone: 'tertiary' },
{ id: 'approval', name: 'Final approval',
start: '2026-11-02', end: '2026-11-02', milestone: true }
]
});Basic usage
Import Gantt and the framework stylesheet. The component creates the calendar, labels, and task bars inside an empty host.
<div id="schedule" class="gantt no-autoinit"></div>import { Gantt } from 'kmaterialize';
import 'kmaterialize/dist/css/materialize.css';
const chart = Gantt.init(document.querySelector<HTMLElement>('#schedule')!, {
label: 'Project schedule',
tasks: [
{ id: 'design', name: 'Design', start: '2026-10-05', end: '2026-10-09', progress: 60 },
{ id: 'build', name: 'Build', start: '2026-10-08', end: '2026-10-16', progress: 20 },
{ id: 'release', name: 'Release', start: '2026-10-19', end: '2026-10-19', milestone: true }
],
onTaskClick: task => console.log(task)
});Browser bundles expose the same component as M.Gantt. For automatic initialization, use class="gantt" and pass the data with M.AutoInit(document.body, { Gantt: { tasks } }). Keep no-autoinit on hosts you initialize yourself.
Move, resize, and zoom
Set editable: true to enable pointer and keyboard edits. Dragging a task preserves its duration; the two end handles change its start or end date. Dates snap to whole calendar days at every zoom level, and a task always occupies at least one day. Milestones can move but keep their single date. Hold a dragged task near a timeline edge to scroll further.
Select several tasks to edit them together. Every selected task moves by the same number of days, preserving the gaps between tasks. Group trimming changes the same end of each selected task by the same amount; shortening stops when the shortest task reaches one day. Selected milestones move with the group but are skipped when resizing. Escape cancels the entire group preview.
const chart = Gantt.init(document.querySelector<HTMLElement>('#schedule')!, {
tasks,
editable: true,
zoom: 1,
minZoom: 0.5,
maxZoom: 3,
onTaskChange: ({ task, previousTask, action }) => {
// Called per changed task after the whole edit commits.
console.log(action, previousTask.start, task.start, task.end);
}
});
chart.setEditable(false); // View-only mode
chart.setEditable(true);
chart.isEditable(); // true
chart.setSelectedTaskIds(['design', 'build']);
chart.getSelectedTaskIds(); // ['design', 'build']
chart.setSelectedTaskIds([]); // Clear selection
chart.setZoom(1.5); // 150% calendar width; labels keep their size
chart.getZoom(); // 1.5
chart.resetZoom(); // 100%, limited by minZoom / maxZoomonTaskChange receives independent task and previousTask copies, plus an action of 'move', 'resize-start', or 'resize-end'. A group edit commits all dates together, then calls this callback once for each changed task; getTasks() already contains the entire update. Canceled drags and unchanged dates do not emit changes. Escape, an interrupted pointer gesture, or leaving the window cancels the preview. Programmatic setTasks() updates do not call this callback.
onSelectionChange(ids) reports the selected IDs in row order. Selection survives zoom, view changes, and task updates; removed tasks are dropped from the selection. The arrow-key move and resize controls apply to the current selection too.
Zoom works in both day and week views and keeps the center date in place when the scroll bounds allow it. Values are clamped to the configured limits. Changing view, zoom, edit mode, or data during a drag cancels that unfinished edit. On touch screens, drag a task or its visible end handles; swipe an empty part of the timeline to scroll.
Progress colors
Without any color configuration, progress is blue below 30%, green from 30% to below 90%, orange from 90% through 100%, and red above 100%. Set progress: 125 to show 125% of expected time, with a visible “25% over expected” label. The colored fill stops at the end of the scheduled bar; the stored value, accessible label, and tooltip retain the full percentage. Progress changes do not change scheduled dates.
const tasks = chart.getTasks();
tasks[0].progress = 125; // 25% over expected; red by default
chart.setTasks(tasks);Give a task's progress line a fixed progressColor, or change its color by percentage using progressMeter. Names, hex colors, and CSS theme variables are supported. The task bar keeps its own tone.
const tasks: GanttTask[] = [
{
id: 'design', name: 'Design', start: '2026-10-05', end: '2026-10-09',
progress: 60,
progressColor: 'var(--md-sys-color-tertiary)'
},
{
id: 'build', name: 'Build', start: '2026-10-08', end: '2026-10-16',
progress: 25,
progressMeter: {
low: 30,
high: 70,
optimum: 100,
optimalColor: 'seagreen',
suboptimalColor: 'darkorange',
criticalColor: 'crimson'
}
}
];
chart.setTasks(tasks);The optional low, high, and optimum settings override the default regions up to 100%. Low and high are required when supplying progressMeter; optimum defaults to 100. All thresholds must be within 0–100, and low must not exceed high. Progress values themselves may exceed 100%; overruns always use the critical region.
| Optimum setting | Optimal range | Other ranges |
|---|---|---|
| Above high | High through 100 | Low up to high is suboptimal; below low is critical. |
| Below low | 0 through low | Above low through high is suboptimal; above high is critical. |
| Between low and high, inclusive | Low through high | Values outside that range are suboptimal. |
Default colors are blue (dodgerblue), green (seagreen), orange (darkorange), and red (crimson). Customize them on the host with --gantt-progress-low-color, --gantt-progress-optimal-color, --gantt-progress-suboptimal-color, and --gantt-progress-critical-color. An explicit progressColor overrides all region colors, including overruns. The task's accessible label and tooltip include its percentage and configured range, and overruns also have a visible text label. Milestones have no progress line.
Task data
import type { GanttTask, GanttOptions, GanttView, GanttTone } from 'kmaterialize';
const task: GanttTask = {
id: 'design',
name: 'Interface design',
start: '2026-10-05',
end: '2026-10-09',
progress: 60,
detail: 'Design team',
tone: 'secondary'
};| Field | Description |
|---|---|
id, name | Required non-empty strings. IDs must be unique within the chart. |
start, end | Required valid YYYY-MM-DD calendar dates. The end is inclusive: a task beginning and ending on the same date occupies one day. End cannot precede start. |
progress | Optional finite, non-negative percentage; defaults to 0. Values above 100 show an overrun. The fill is capped at the scheduled bar width while labels and data preserve the actual percentage. |
progressColor | Optional CSS color for the progress line, independent of the task tone. Takes precedence over meter colors. |
progressMeter | Optional GanttProgressMeter with low, high, optimum, and configurable region colors. See Progress colors. |
detail | Optional secondary label, such as the assignee or team. |
tone | 'primary' (default), 'secondary', 'tertiary', or 'error'. Uses your Material color tokens. |
milestone | Set to true for a diamond. Start and end must be the same date. |
dependencies | Optional array of upstream task IDs. Links allow overlaps and propagate moves only to downstream tasks. See Dependencies. |
The range fits the tasks automatically, with padding at both ends. The maximum span between the earliest start and latest end is 3,661 calendar days. Invalid data throws an error and leaves an existing chart intact. Task names and details are rendered as text.
Options
| Option | Default | Description |
|---|---|---|
tasks | [] | Task data in display order. An empty array shows the empty state. |
view | 'day' | Daily columns, or compact weekly intervals with 'week'. |
editable | false | Enable moving and resizing tasks with pointer or keyboard controls. |
connectable | true | Show drag, click/tap, and keyboard connection handles and dependency removal buttons when editable. Set to false to keep date editing without direct link editing. Programmatic dependency methods remain available. |
zoom | 1 | Initial calendar column scale; 1 is 100%. |
minZoom, maxZoom | 0.5, 3 | Positive zoom limits; minimum must not exceed maximum. |
onTaskChange | null | Receives { task, previousTask, action } after a user commits changed dates. Import GanttTaskChange and GanttEditAction for the callback types. |
onSelectionChange | null | Receives a copy of the selected task IDs after selection changes. |
onDependencyChange | null | Receives { from, to, action } after a direct connection/removal or a call to addDependency() or removeDependency() changes a link. |
locale | 'en' | Locale used to format the calendar, such as 'fr'. |
label | 'Project timeline' | Visible heading and accessible name. |
showToday | true | Highlights the user's local today when it falls within the task range. |
emptyText | 'No tasks scheduled.' | Message for an empty schedule. |
onTaskClick | null | Receives (task, event) when a task or milestone is activated by pointer or keyboard. The task is a copy. |
Update a schedule
Use the public methods to connect the chart to your own task editor or saved data. Dates can be edited directly in edit mode or replaced through setTasks().
const tasks = chart.getTasks();
tasks[0].progress = 80;
chart.setTasks(tasks);
chart.setView('week');
chart.getView(); // 'week'
chart.scrollToDate('2026-10-12');
chart.scrollToDate(); // Today
Gantt.getInstance(document.querySelector<HTMLElement>('#schedule')!);
chart.setTasks([]); // Show the empty state
chart.destroy(); // Remove listeners and restore the original host contentgetTasks() and callbacks return copies, and setTasks() copies the supplied data. Initializing the same host again replaces its previous instance. All styling comes from kmaterialize, including light and dark themes. Adjust --gantt-label-width on the host to customize the task column width.