Trend / Intermediate

Gantt Chart

Compare task dates, durations, and overlaps on a calendar timeline.

Back to collection

Interactive Chart Preview

JAVASCRIPT
Live, interactive exampleChart.js

Chart.js Code Example

JAVASCRIPT
import Chart from 'chart.js/auto';

// Illustrative data used by the live preview.
const chartData = {
  "taskHeaders": [
    "Task ID",
    "Task",
    "Start",
    "End (Exclusive)",
    "Duration (Days)"
  ],
  "tasks": [
    {
      "id": "T1",
      "label": "Requirements",
      "start": "2025-01-06",
      "end": "2025-01-09"
    },
    {
      "id": "T2",
      "label": "Design",
      "start": "2025-01-08",
      "end": "2025-01-13"
    },
    {
      "id": "T3",
      "label": "Build",
      "start": "2025-01-13",
      "end": "2025-01-20"
    },
    {
      "id": "T4",
      "label": "Review",
      "start": "2025-01-17",
      "end": "2025-01-22"
    },
    {
      "id": "T5",
      "label": "Release",
      "start": "2025-01-22",
      "end": "2025-01-23"
    }
  ],
  "datasets": []
};

function createConfig(chartData) {
    const rows = chartData.tasks, headers = chartData.taskHeaders, day = 86400000;
    const utc = (date) => Date.parse(`${date}T00:00:00.000Z`);
    const starts = rows.map(row => utc(row.start)), ends = rows.map(row => utc(row.end));
    const minimum = Math.min(...starts), maximum = Math.max(...ends);
    const step = Math.max(1, Math.ceil((maximum - minimum) / day / 4)) * day;
    const ticks = Array.from({ length: Math.floor((maximum - minimum) / step) + 1 }, (_, index) => minimum + index * step);
    const dateLabel = (value) => new Date(value).toISOString().slice(0, 10);
    const label = (index) => `${rows[index].label} (${rows[index].id})`;
    const shortLabel = (index) => label(index).length > 22 ? label(index).slice(0, 21) + '…' : label(index);
    return {
        type: 'bar', data: { labels: rows.map((_, index) => label(index)), datasets: [{ label: 'Task Interval', data: rows.map((_, index) => [starts[index], ends[index]]), backgroundColor: '#3b82f6', categoryPercentage: 0.6, barPercentage: 1, maxBarThickness: 32 }] },
        options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false,
            scales: {
                x: { type: 'linear', min: minimum, max: maximum, afterBuildTicks: scale => { scale.ticks = ticks.map(value => ({ value })); }, ticks: { callback: value => dateLabel(Number(value)), maxRotation: 45 }, title: { display: true, text: 'Calendar Date (UTC)' } },
                y: { grid: { display: false }, ticks: { autoSkip: true, maxTicksLimit: 12, callback: value => shortLabel(Number(value)) } },
            },
            plugins: { legend: { display: false }, title: { display: true, text: 'End Date Is Exclusive' }, tooltip: { callbacks: {
                        title: items => label(items[0].dataIndex),
                        label: item => { const i = item.dataIndex; return [`${headers[2]}: ${rows[i].start}`, `${headers[3]}: ${rows[i].end}`, `${headers[4]}: ${(ends[i] - starts[i]) / day}`]; },
                    } } },
        },
    };
}

const chart = new Chart(document.getElementById('myChart'), createConfig(chartData));

Preview and code follow the same library selection.

Run This Example

Install the package below in a browser project such as Vite. Put the copied code in a client JavaScript module and add this container to the page before the module runs.

npm install chart.js@4.5.1
<div style="height:320px"><canvas id="myChart"></canvas></div>

The example includes its sample data. When removing the chart, destroy or dispose of its instance and disconnect any resize observer.

Reading This Example

This illustrative five-task schedule runs from 6 January to the exclusive end boundary on 23 January 2025. Requirements lasts three days, Design five, Build seven, Review five, and Release one. Requirements and Design overlap on 8 January. Build and Review overlap from 17 to 20 January, with 20 January excluded. The overall timeline spans 17 days; adding task durations would count overlapping days more than once.

All library tabs use these illustrative values. Rendering styles and library APIs differ, so use the table to compare exact numbers.

About this example & sample data
Sample values used in the chart
Task IDTaskStartEnd (Exclusive)Duration (Days)
T1Requirements2025-01-062025-01-093
T2Design2025-01-082025-01-135
T3Build2025-01-132025-01-207
T4Review2025-01-172025-01-225
T5Release2025-01-222025-01-231

READ THE CHART

Read Task Intervals on a Calendar

A Gantt Chart places each task on its own row and draws a bar from its start to its end. Horizontal position shows when work is scheduled; width shows elapsed duration. Compare rows vertically to see which intervals overlap.

01

Read the End Date as a Boundary

These examples include the start date and exclude the end date. A task from 6 to 9 January covers 6, 7, and 8 January, a duration of three days. A one-day task therefore ends the next day. A task ending on the same date another starts does not overlap that next task. The calculated duration uses this rule in every library.

02

Keep Calendar Spacing Intact

Dates use a shared elapsed-time scale: a seven-day task is seven times as wide as a one-day task. Weekends and holidays count, and gaps between tasks occupy real horizontal space. Inputs are whole UTC calendar dates with no times of day or daylight-saving adjustments. These durations measure calendar days, not working days or effort.

03

Keep Task Identity Separate From Names

Each row needs a unique task ID, while display names may repeat. The chart keeps the table order and shows the name beside its ID. Renaming a task does not combine it with another row. Short previews may shorten or omit crowded axis labels; the table and copied examples retain every task and its original dates.

04

Avoid Inferring Dependencies

Overlapping bars show shared time, not whether people or resources can handle the work. This editor contains dates and task labels only. It does not define dependencies, completion percentages, milestones, a business-day calendar, or a critical path. Use a Line Chart for measured values over time. The Seaborn example applies a theme to Matplotlib horizontal bars; Seaborn has no dedicated Gantt function here.

Edit Chart Data