Gantt
A project timeline with draggable task bars, dependencies, milestones, hierarchy, and a real commit gate. Renders with a Razor-first engine — no third-party charting library.
Installation
dotnet add package Lumeo.Gantt
One-time app setup (AddLumeo(), CSS & JS) is covered in the
installation guide.
Usage
@using Lumeo <GanttChart />
GanttChart is a from-scratch Razor + minimal-JS engine shipping in Lumeo.Gantt.
Zero JS charting dependencies, zero CDN downloads, zero theming surprises. All MIT.
The original SVG-based Gantt component is still shipped for existing consumers —
see Legacy: Gantt (v2).
When to Use
- Planning a project with ordered phases, cross-task dependencies, and milestones.
- Grouping work by swim-lane, or modeling real parent/child hierarchy with a tree pane.
- Letting stakeholders see "what blocks what" at a glance, drag to reschedule, and drag the progress handle to mark completion.
- Enforcing your own scheduling rules — reject an invalid drag, or silently snap it to a grid — via the
OnTaskUpdatecommit gate.
Four phases (ParentId hierarchy, tree pane on automatically), cross-phase
Dependencies, and two IsMilestone diamonds — "Kickoff approved" and
"Go-live". This is also Day mode's own upper header row: the "W32 Aug 3 – 9"-style
bands are ISO-8601 week numbers, automatic in every Day-mode demo on this page.
OnTaskUpdate is a gate, not a notification: it can reject a drag
(false), accept it as proposed (true), or
accept it with an adjustment (GanttUpdateResult.AcceptWith(...)) that
snaps the committed Start/End to different values. Try dragging a bar here — this demo rejects
anything that would start before today, and snaps every other drop onto the nearest Monday.
Drag the progress handle at the base of a bar to change completion. OnProgressChange
fires the v2-parity notification; OnTaskUpdate (Source=Progress) gates the
same edit like every other gesture.
Use the built-in toolbar to switch between 7 zoom levels — QuarterDay, HalfDay,
Day, Week, Month, Quarter, and Year.
ShowZoomControl renders the segmented switcher shown here.
Pass a GroupBy function and/or set GroupLabel on each GanttTask.
Tasks are sorted by group and a section-header row is injected before the first task of each group.
Set GanttTask.ParentId to build real parent/child hierarchy. The tree pane shows the
task-name column plus any extra GanttTreeColumns you supply (here, a typed "Progress"
column) — drag the divider at the pane's trailing edge to resize it.
Set Readonly="true" to lock the chart — bars can't be dragged, resized, or created.
Use the BarColor delegate to return any CSS colour (hex, rgb, or a CSS variable like
var(--color-destructive)) per task — or set ColorByGroup="true" (shown in the
settings-menu demo above) for automatic per-group palette colouring.
NowIndicator draws a precise current-time line — only meaningful at sub-day
granularity, so this demo starts in HalfDay zoom (12-hour columns); switch to
Day/Week/etc. via the toolbar and it disappears, same as it would in your
own app. MarkOffDays tints weekend columns using OffDays (or a
region-appropriate default derived from the current culture) at any zoom level.
ShowRowCheckboxes renders a checkbox column in the tree pane. Only LEAF tasks are ever
members of SelectedIds — checking a parent phase toggles every descendant, not the
phase itself.
AllowRowReorder lets you drag rows in the tree pane to re-sequence a backlog. Reorder is
scoped to siblings under the same parent (or the root level here, since these tasks have none) —
OnRowReorder fires once per completed drop with the resolved before/after index.
RowTemplate replaces the tree pane's default name-cell content entirely — here, a
status dot (derived from Progress) plus the task name, instead of plain text.
API Reference
GanttChart
GanttTask
| Field | Type | Description |
|---|---|---|
| Id | string | Stable identifier used to reconcile drag edits back into the caller's collection. |
| Name | string | The label rendered on the task bar and in the tree pane's name column. |
| Start | DateTime | Start date (inclusive). |
| End | DateTime | End date. |
| Progress | int | Completion percentage, 0–100. Drives the progress fill and the progress-handle drag. |
| Dependencies | string[]? | Ids of tasks this task depends on. Rendered as routed arrows between bars. |
| CustomClass | string? | Extra CSS class applied to the bar — use to colour-code milestones or delayed work. |
| IsMilestone | bool | When true, renders as a diamond at the Start date. End is clamped to Start. |
| GroupLabel | string? | Optional swim-lane label — cluster with GroupBy. |
| ParentId | string? | GanttChart-only: set to another task's Id to build real parent/child hierarchy and enable the tree pane by default. |
| IsRecurring | bool | Marks the bar as part of a series you generated yourself — see Styling hooks' data-recurring. |
GanttUpdateResult / GanttUpdateAdjustment
The verdict OnTaskUpdate returns for every drag/resize/progress/create commit.
| Value | Effect |
|---|---|
| false / GanttUpdateResult.Reject | Rejects the edit outright — nothing commits, no callback fires, no TasksChanged. |
| true / GanttUpdateResult.Accept | Commits exactly the proposed Start/End/Progress. |
| GanttUpdateResult.AcceptWith(adjustment) | Commits adjustment's Start/End/Progress instead of the raw proposal (any left null keeps the proposed value). |
GanttViewMode
| Value | Description |
|---|---|
| QuarterDay | 6-hour columns — useful for hour-scale shift scheduling. |
| HalfDay | 12-hour columns. |
| Day | One column per day — the default. |
| Week | One column per week. |
| Month | One column per month — best for multi-quarter plans. |
| Quarter | One column per 3-month quarter — GanttChart-only. |
| Year | One column per year — best for roadmap-style views. |
Styling hooks (data-*)
A rendered bar, grid cell, or drag ghost carries plain data-*
attributes for its own state — no class-string parsing required. Boolean states are presence-only
(the attribute is either there or it isn't, so [data-selected]
is a valid selector on its own); data-progress
is the one exception and carries the clamped 0–100 value.
| Attribute | Element | Shape | Meaning |
|---|---|---|---|
| data-selected | Bar | presence | The task's id is in SelectedIds (e.g. checked via a row checkbox). |
| data-dragging | Bar | presence | A move/resize/progress drag gesture is live on this bar (set by the drag engine, not by a re-render). |
| data-progress | Bar | 0–100 | The task's clamped completion percentage — the same value the progress fill's width uses. |
| data-completed | Bar | presence | Progress is exactly 100. |
| data-past | Bar | presence | The task's End is before "now" (the browser's current time, not the server's). |
| data-recurring | Bar | presence | GanttTask.IsRecurring is set — for a consumer generating its own repeating occurrences. |
| data-off | Header cell / canvas band | presence | An off-day column under MarkOffDays/OffDays — on the header date cell and the canvas tint behind it. |
| data-drop-invalid | Drag ghost | presence | The current drag position fails CanDrop — including while a verdict is still in flight (fail-closed). |
One example per family — a boolean bar state, the value-carrying one, and the grid/drag hooks:
/* Boolean bar state — outline the selected task */
[data-selected] { outline: 2px solid var(--color-primary); }
/* Value-carrying — a completion badge past 80% */
[data-progress] { --p: attr(data-progress number); }
[data-progress]:is([data-completed]) { opacity: 0.7; }
/* Grid / drag ghost */
[data-off] { background: color-mix(in oklab, var(--color-muted) 60%, transparent); }
[data-drop-invalid] { border-color: var(--color-destructive); }Related Components
- Scheduler — calendar-style event grid when you care about time-of-day rather than project phases.
- Data Grid — tabular view of the same task data when a timeline isn't needed.
- Gantt (Legacy) — the original SVG-based engine, kept for backward compatibility.