Building Gantt Diagrams for Project Management on Websites
A Gantt diagram displays project tasks on a timeline: start/end dates, task dependencies, completion progress, and resource utilization.
Libraries
| Library | Features |
|---|---|
| DHTMLX Gantt | Commercial, feature-rich, drag-drop |
| Bryntum Gantt | Commercial, React components |
| frappe-gantt | Open-source, simple |
| @dhtmlx/gantt | npm package for DHTMLX |
| gantt-task-react | Open-source React |
gantt-task-react (open-source)
npm install gantt-task-react
import { Gantt, Task, ViewMode } from 'gantt-task-react';
import 'gantt-task-react/dist/index.css';
interface ProjectTask {
id: string;
name: string;
start: Date;
end: Date;
progress: number;
type: 'task' | 'milestone' | 'project';
dependencies?: string[];
assignee?: string;
}
function ProjectGantt({ tasks, onTaskChange }: {
tasks: ProjectTask[];
onTaskChange: (task: Task) => void;
}) {
const [view, setView] = useState<ViewMode>(ViewMode.Week);
const ganttTasks: Task[] = tasks.map(t => ({
id: t.id,
name: t.name,
start: t.start,
end: t.end,
progress: t.progress,
type: t.type,
dependencies: t.dependencies ?? [],
styles: {
progressColor: t.progress >= 100 ? '#22c55e' : '#3b82f6',
progressSelectedColor: '#1d4ed8'
}
}));
return (
<div>
<div className="gantt-toolbar">
<button onClick={() => setView(ViewMode.Day)}>Day</button>
<button onClick={() => setView(ViewMode.Week)}>Week</button>
<button onClick={() => setView(ViewMode.Month)}>Month</button>
</div>
<Gantt
tasks={ganttTasks}
viewMode={view}
onDateChange={onTaskChange}
onProgressChange={onTaskChange}
locale="en"
listCellWidth="200px"
ganttHeight={400}
todayColor="rgba(59, 130, 246, 0.1)"
TooltipContent={({ task }) => (
<div className="gantt-tooltip">
<strong>{task.name}</strong>
<p>Start: {format(task.start, 'dd.MM.yyyy')}</p>
<p>End: {format(task.end, 'dd.MM.yyyy')}</p>
<p>Progress: {task.progress}%</p>
</div>
)}
/>
</div>
);
}
Custom Gantt on SVG + D3
For specific requirements (custom styling, non-standard cells):
import { scaleTime } from 'd3-scale';
import { timeDay, timeWeek } from 'd3-time';
function CustomGantt({ tasks, startDate, endDate, width = 900 }) {
const timelineWidth = width - 200;
const rowHeight = 40;
const height = tasks.length * rowHeight + 60;
const xScale = scaleTime()
.domain([startDate, endDate])
.range([0, timelineWidth]);
return (
<svg width={width} height={height}>
{timeWeek.range(startDate, endDate).map(week => (
<g key={week.toISOString()}>
<line
x1={xScale(week) + 200} y1={0}
x2={xScale(week) + 200} y2={height}
stroke="#e5e7eb" strokeWidth={1}
/>
<text
x={xScale(week) + 204}
y={15}
fontSize={11}
fill="#6b7280"
>
{format(week, 'dd MMM')}
</text>
</g>
))}
{tasks.map((task, i) => {
const x = xScale(task.start) + 200;
const width = xScale(task.end) - xScale(task.start);
const y = i * rowHeight + 25;
return (
<g key={task.id}>
<text x={4} y={y + 14} fontSize={13} fill="#374151"
style={{ cursor: 'pointer' }}>
{task.name.length > 22 ? task.name.slice(0, 22) + '…' : task.name}
</text>
<rect x={x} y={y} width={width} height={24}
rx={4} fill="#93c5fd" />
<rect x={x} y={y} width={width * (task.progress / 100)} height={24}
rx={4} fill="#3b82f6" />
{width > 40 && (
<text x={x + width / 2} y={y + 16}
textAnchor="middle" fontSize={11} fill="white">
{task.progress}%
</text>
)}
</g>
);
})}
</svg>
);
}
Drag-and-drop for Date Changes
Using DHTMLX Gantt drag-drop is built-in. For custom SVG — d3-drag or @dnd-kit.
Timeline
gantt-task-react with custom tooltip — 3–5 days. Custom SVG Gantt with drag-drop and dependencies — 2–3 weeks.







