ApexCharts Gantt Timeline with Range Bars — Free Project Plan Snippet
ApexCharts Gantt Timeline with Range Bars · Charts · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="agt-wrap">
<div class="agt-card">
<div class="agt-head">
<div>
<div class="agt-title">Website Relaunch — Q4 Plan</div>
<div class="agt-sub">Bars are coloured by owner team · the dashed line is today</div>
</div>
<button class="agt-btn" id="agtSlip" type="button">Slip design by 1 week</button>
</div>
<div id="agtChart"></div>
</div>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#f8fafc;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.agt-wrap{width:100%;max-width:900px}
.agt-card{background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:20px 18px 8px;box-shadow:0 1px 8px rgba(15,23,42,.06)}
.agt-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap}
.agt-title{font-size:15px;font-weight:700;color:#0f172a}
.agt-sub{font-size:12px;color:#64748b;margin-top:3px}
.agt-btn{border:1px solid #cbd5e1;background:#fff;border-radius:9px;padding:7px 12px;font:600 12px system-ui;color:#0f172a;cursor:pointer}
.agt-btn:hover{border-color:#6366f1;color:#4f46e5}function d(s) { return new Date(s + 'T00:00:00Z').getTime(); }
var WEEK = 7 * 86400000;
var TEAM_COLORS = { Research: '#8b5cf6', Design: '#ec4899', Engineering: '#0ea5e9', Marketing: '#f59e0b' };
// Each task: a row label (x), a [start, end] range (y) and an owner team.
// Tasks with dependsOn start no earlier than their dependency ends.
var TASKS = [
{ x: 'User interviews', start: d('2026-10-01'), end: d('2026-10-14'), team: 'Research' },
{ x: 'Information arch.', start: d('2026-10-12'), end: d('2026-10-23'), team: 'Research', dependsOn: 'User interviews' },
{ x: 'Visual design', start: d('2026-10-19'), end: d('2026-11-06'), team: 'Design', dependsOn: 'Information arch.' },
{ x: 'Design system tokens', start: d('2026-10-26'), end: d('2026-11-09'), team: 'Design' },
{ x: 'Frontend build', start: d('2026-11-02'), end: d('2026-11-27'), team: 'Engineering', dependsOn: 'Visual design' },
{ x: 'CMS migration', start: d('2026-11-09'), end: d('2026-11-30'), team: 'Engineering' },
{ x: 'QA & accessibility', start: d('2026-11-23'), end: d('2026-12-07'), team: 'Engineering', dependsOn: 'Frontend build' },
{ x: 'Launch campaign', start: d('2026-12-01'), end: d('2026-12-14'), team: 'Marketing', dependsOn: 'QA & accessibility' },
];
// Push dependants forward so none starts before its dependency ends,
// keeping each task's duration. Tasks are listed in dependency order.
function resolve() {
var byName = {};
TASKS.forEach(function (t) {
if (t.dependsOn && byName[t.dependsOn] && t.start < byName[t.dependsOn].end) {
var len = t.end - t.start;
t.start = byName[t.dependsOn].end;
t.end = t.start + len;
}
byName[t.x] = t;
});
}
function series() {
return [{
data: TASKS.map(function (t) {
return { x: t.x, y: [t.start, t.end], fillColor: TEAM_COLORS[t.team], meta: t.team };
}),
}];
}
var TODAY = d('2026-11-04');
var fmt = function (ts) { return new Date(ts).toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' }); };
resolve();
var chart = new ApexCharts(document.getElementById('agtChart'), {
chart: { type: 'rangeBar', height: 360, toolbar: { show: false }, fontFamily: 'system-ui, sans-serif' },
series: series(),
plotOptions: {
bar: {
horizontal: true,
barHeight: '58%',
borderRadius: 5,
rangeBarGroupRows: false,
},
},
xaxis: { type: 'datetime', labels: { datetimeUTC: true, format: 'dd MMM' } },
yaxis: { labels: { style: { fontSize: '12px', colors: '#334155' } } },
grid: { borderColor: '#eef2f7', xaxis: { lines: { show: true } }, yaxis: { lines: { show: false } } },
dataLabels: {
enabled: true,
style: { fontSize: '11px', fontWeight: 600 },
formatter: function (val) {
var days = Math.round((val[1] - val[0]) / 86400000);
return days + 'd';
},
},
annotations: {
xaxis: [{
x: TODAY,
borderColor: '#ef4444',
strokeDashArray: 4,
label: { text: 'Today', orientation: 'horizontal', style: { background: '#ef4444', color: '#fff', fontSize: '11px' } },
}],
},
tooltip: {
custom: function (o) {
var p = o.w.config.series[o.seriesIndex].data[o.dataPointIndex];
return '<div style="padding:8px 10px;font:12px system-ui">' +
'<b>' + p.x + '</b><br>' + p.meta + '<br>' + fmt(p.y[0]) + ' → ' + fmt(p.y[1]) + '</div>';
},
},
legend: { show: false },
});
chart.render();
document.getElementById('agtSlip').addEventListener('click', function () {
var t = TASKS.find(function (x) { return x.x === 'Visual design'; });
t.start += WEEK;
t.end += WEEK;
resolve();
chart.updateSeries(series());
});Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
ApexCharts Gantt Chart — Range Bars, a Today Line and Simple Dependencies

A Gantt chart answers two questions at once: what happens when, and what happens if something runs late. ApexCharts doesn't ship a dedicated Gantt type, but its rangeBar series with horizontal: true is exactly a list of rows with a start and end on a time axis.
Each point is a row with a range
Every task becomes one data point: x is the row label, y is a [start, end] pair of timestamps. fillColor sets each bar's colour individually, so bars are coloured by owner team without splitting the data into one series per team (which would break row ordering).
Durations and dates without clutter
The data label formatter receives the [start, end] range and prints the duration in days inside each bar. Exact dates live in a custom tooltip, which reads the full data point — including the extra meta field holding the team name.
A today marker
An x-axis annotation draws a dashed red line at the current date with a label. This is often the most-used part of a real project chart: everything left of it should be finished.
Dependencies, kept deliberately simple
Tasks can declare dependsOn. The resolve() function walks the tasks in order and moves any dependant whose start falls before its dependency's end, preserving its duration. "Slip design by 1 week" moves Visual design and re-resolves, so Frontend build, QA and the launch campaign cascade forward. This is a teaching-sized scheduler: it assumes tasks are listed in dependency order and it does not draw dependency arrows.
Dates in UTC
Dates are created with an explicit UTC midnight and the axis uses datetimeUTC: true, so bars don't shift by a day for viewers in other time zones.
Step by step
How to Use
- 1Load ApexChartsInclude apexcharts.min.js from the CDN.
- 2Paste the snippetEight tasks render as horizontal bars on a date axis.
- 3Hover a barThe tooltip shows task, team and exact dates.
- 4Slip a taskClick the button; dependent tasks move forward automatically.
- 5Edit the planChange TASKS: label, start, end, team and optional dependsOn.
- 6Move the today lineSet TODAY to the current date, for example Date.now().
Real-world uses
Common Use Cases
rangeBar structure with horizontal: true and team-coloured points.Got questions?
Frequently Asked Questions
Not as a named type, but a rangeBar series with plotOptions.bar.horizontal set to true renders one bar per row between a start and end value on a datetime axis, which is a Gantt chart.
Set fillColor on each data point. Using one series per category also works, but it can change how rows are grouped and ordered, so per-point colours are simpler for a task list.
Add an entry to annotations.xaxis with x set to the current timestamp, a borderColor and strokeDashArray for a dashed line, and a label.
Each task may name the task it depends on. resolve() walks tasks in order and, if a dependant starts before its dependency ends, moves it to start at that end while keeping its duration. For complex plans with arrows and critical paths, use a dedicated Gantt library.
new Date('2026-10-01') parses as UTC, but other formats parse as local time. Mixing them, or formatting in local time, can shift bars by a day for some viewers. Using explicit UTC dates and datetimeUTC on the axis keeps every viewer on the same calendar day.