Framework wrapper detection — check
package.jsonbefore generating code.
react→ usereact-apexganttinstead of the core API.vue→ usevue-apexgantt.@angular/core→ usengx-apexgantt.Wrappers handle
destroy()automatically on unmount, accept reactive props, and forward events as idiomatic framework events. Use the core API directly only when no framework is detected, or when the user explicitly asks for vanilla. Seereferences/framework-wrappers.md.
1. Critical Rules
- Always call
gantt.render()afternew ApexGantt(el, options). The constructor builds internal state but does not paint the DOM. - Always call
gantt.destroy()before unmounting in React / Vue / Angular. The chart attachesResizeObservers and global event listeners — skippingdestroy()causes memory leaks and "ghost" event handlers. seriesis the task array (required). It is NOT optional and NOT the same shape as ApexCharts series. Each task is an object withid,name,startTime, and (usually)endTime— see the data format in §2.- Date strings must match
inputDateFormat. The default is'MM-DD-YYYY'. If you pass'2026-06-01'(ISO), setinputDateFormat: 'YYYY-MM-DD'or the parse will fail silently and the bar won't render. idmust be unique and stable across renders. Selection, dependency arrows, and the diff-update path all key offid. Re-usingids breaks dependency drawing and selection state.parentIdreferences must point to an existing taskid. Orphan parents are dropped silently from the tree.- Milestones omit
endTime. A task withtype: TaskType.Milestone(or'milestone') renders as a diamond atstartTime; do not passendTimefor milestones. progressis 0–100, not 0–1. Values outside the range are clamped.- Dependencies accept two shapes: a plain task
idstring (treated asFSwithlag: 0) or aTaskDependencyobject{ taskId, type, lag }. Mixing the two on the same task is fine. - Use
update()for full reconfigures,updateTask(id, patch)for surgical single-task changes.updateTaskskips a full re-layout and is much faster for streaming progress updates. - For non-standard data shapes use
parsing, not manual.map(). Theparsingconfig maps your fields →TaskInputfields and supports dot-notation paths andtransformfunctions. enableSelection: trueis required forgetSelectedTasks()/setSelectedTasks()/clearSelection()and theselectionChangeevent to fire.- Custom toolbar items live in
toolbarItems, not in a separate render call. Useposition: 'left'to put them before the built-in zoom/export controls. - Events fire on the host container element, not on the
ganttinstance. Listen viacontainer.addEventListener(GanttEvents.TASK_UPDATE_SUCCESS, …).
2. Task Data Format (series)
series is an array of TaskInput objects:
{
id: string; // REQUIRED, unique, stable
name: string; // REQUIRED, display label
startTime: string; // REQUIRED, parsed by inputDateFormat
endTime?: string; // omit for milestones
progress?: number; // 0–100, default 0
type?: 'task' | 'milestone'; // default 'task'
parentId?: string; // hierarchical nesting
dependency?: string | TaskDependency;
baseline?: { start: string; end: string };
barBackgroundColor?: string; // per-task override
rowBackgroundColor?: string; // per-row override
collapsed?: boolean; // hide children
segments?: { start: string; end: string }[]; // split task into worked spans; see references/data-format.md
}
Split tasks: pass
segments(each{ start, end }) to render one task as several worked spans with gaps on a single row.startTime/endTimebecome the derived envelope. Split at runtime withgantt.splitTask(id, at, { resumeAt }). Seereferences/data-format.md.
Minimal example:
const gantt = new ApexGantt(document.getElementById('chart'), {
series: [
{ id: 't1', name: 'Design', startTime: '01-01-2026', endTime: '01-15-2026', progress: 60 },
{ id: 't2', name: 'Develop', startTime: '01-16-2026', endTime: '02-15-2026', progress: 25, dependency: 't1' },
{ id: 'm1', name: 'Launch', startTime: '02-20-2026', type: 'milestone' }
]
});
gantt.render();
Dependency variants
| Shape | Meaning |
|---|---|
dependency: 't1' |
Plain id string. FS with 0-day lag. |
dependency: { taskId: 't1' } |
FS with 0-day lag (verbose). |
dependency: { taskId: 't1', type: 'SS', lag: 2 } |
Start-to-Start, 2-day lag (delay). |
dependency: { taskId: 't1', type: 'FS', lag: -1 } |
Finish-to-Start, 1-day lead (overlap). |
type values: 'FS' (Finish-to-Start, default), 'SS' (Start-to-Start), 'FF' (Finish-to-Finish), 'SF' (Start-to-Finish).
Hierarchy
Use parentId to nest. Parent rows automatically aggregate children's date range and render an expand/collapse caret. Set collapsed: true on the parent to start collapsed.
series: [
{ id: 'a', name: 'Phase 1', startTime: '01-01-2026', endTime: '02-01-2026' },
{ id: 'a1', name: 'Subtask 1.1', parentId: 'a', startTime: '01-01-2026', endTime: '01-15-2026' },
{ id: 'a2', name: 'Subtask 1.2', parentId: 'a', startTime: '01-16-2026', endTime: '02-01-2026' },
]
Baseline (planned vs. actual)
Enable globally, then attach baseline: { start, end } to any task you want to compare:
const gantt = new ApexGantt(el, {
baseline: { enabled: true, color: '#b0b8c1' },
series: [
{ id: 't1', name: 'Design', startTime: '01-01-2026', endTime: '01-20-2026',
baseline: { start: '01-01-2026', end: '01-15-2026' } }
]
});
3. Top-Level Options
| Option | Type | Default | Notes |
|---|---|---|---|
series |
TaskInput[] |
required | Task data. |
theme |
'light' | 'dark' |
'light' |
Built-in palette. See also the family --apx-* tokens below. |
pixelsPerDay |
number |
auto-fit | Initial zoom as pixels-per-day (continuous; header tiers auto-chosen). Reference values: Year ≈ 0.5, Quarter ≈ 1.6, Month ≈ 4.9, Week ≈ 25.7, Day = 80. Omit to auto-fit the data range. |
inputDateFormat |
string |
'MM-DD-YYYY' |
dayjs format for startTime/endTime. |
snapUnit |
'day' | 'hour' | 'minute' |
'day' |
Granularity that drag / resize / inline edits snap to. 'hour'/'minute' enable sub-day scheduling. See §10. |
snapValue |
number |
1 |
Multiplier on snapUnit (e.g. 'minute' + 15 → 15-min steps). |
calendar |
CalendarOptions |
none | Working-calendar (weekends, holidays, non-working stripes, drag snap). See §10. |
history |
{ enabled, maxSize } |
{ enabled: true, maxSize: 100 } |
Undo/redo stack config. See §11. |
width / height |
number | string |
'100%' / 500 |
Pixel number or CSS string. |
rowHeight |
number |
40 |
px |
tasksContainerWidth |
number |
425 |
Initial task-list panel width in px. |
columnConfig |
ColumnListItem[] |
(all) | Authoritative; only listed columns render. |
autoSizeColumns |
boolean |
true |
Auto-size each column to fit header + content, growing the panel (never below tasksContainerWidth). false = legacy split-by-flexGrow. See references/grid.md. |
resizableColumns |
boolean |
true |
Drag a column-header handle to pin a column to an exact px width. |
reorderableColumns |
boolean |
true |
Drag a column header to reorder columns. |
sortBy |
SortCriterion | SortCriterion[] |
start-time asc | Initial sort (single or multi-key), hierarchy-preserving. Pass [] for natural (input) order. |
filterBy |
(task) => boolean |
— | Initial predicate filter (keeps matches + their ancestors). |
filterRules |
FilterRuleSet |
— | Initial structured filter { match: 'all'|'any', rules }. Takes precedence over filterBy. |
enableQuickFilter |
boolean |
false |
Toolbar quick-filter search box. |
quickFilter |
QuickFilterOptions |
— | Tune the quick filter: placeholder, fields (default ['name']), caseSensitive. |
enableFilterBuilder |
boolean |
false |
Toolbar advanced filter builder (All/Any popover). |
groupBy |
GroupCriterion | ColumnKey | string |
— | Initial grouping by a column value. Suspends the tree into collapsible group headers. See references/grid.md. |
enableTaskDrag |
boolean |
true |
Reorder rows by dragging. |
enableTaskResize |
boolean |
true |
Resize bars by dragging handles. |
enableTaskEdit |
boolean |
false |
Inline edit form on row click. |
enableInlineEdit |
boolean |
false |
Edit name/startTime/endTime/duration/progress directly in list cells. Auto-on when enableTaskEdit is true unless set false. |
enableProgressDrag |
boolean |
true |
Drag the in-bar handle to set progress. Fires taskProgressChanged. |
enableSelection |
boolean |
false |
Multi-row selection. Required for selection APIs. |
enableTaskEditingShortcuts |
boolean |
false |
Delete/Backspace delete focused row; Tab/Shift+Tab indent/outdent. Needs list focus. |
enableTaskCRUDToolbar |
boolean |
false |
Built-in + Add Task / trash Delete toolbar buttons. Delete needs enableSelection. |
enableContextMenu |
boolean |
false |
Right-click menu: Edit, Add child/sibling, Indent, Outdent, Delete (capability-gated). |
enableAddTaskRow |
boolean |
false |
+ Add task row at the bottom of the list. Disabled while virtualising (≥ 50 rows). |
enableDrawTask |
boolean |
false |
Draw a new task by dragging across empty timeline space (snaps to snapUnit/snapValue). See references/interaction.md. |
enableScrollButtons |
boolean |
false |
Edge chevrons that jump to an off-screen bar. (Default flipped to false in 3.15.0.) |
scrollToTaskOnRowClick |
boolean |
false |
Clicking a task-list row scrolls its bar into view (nearest-edge). Composes with enableSelection. (New in 3.15.0.) |
persistState |
boolean | { key } |
false |
Persist/restore UI view state via localStorage (true = default key 'apexgantt-state'). See references/interaction.md. |
exportFormat |
'svg' | 'png' | 'pdf' |
'svg' |
Format for the toolbar export button. Any format is also available via gantt.exportChart(format). |
enableCriticalPath |
boolean |
false |
Compute & highlight CPM through dependencies. |
enableRollups |
boolean |
false |
Thin rollup markers under summary bars at each leaf's range (visible even when collapsed). |
enableProjectBoundary |
boolean |
false |
Two vertical lines at the project's earliest start / latest end. |
projectBoundaryColor |
string |
'#7C3AED' |
Stroke for the boundary lines. Falls back to annotationBorderColor. |
enableCrosshair |
boolean |
false |
Vertical cursor-following line + date/time label. |
crosshairColor |
string |
theme accent | Crosshair line and label-background color. |
crosshairLabelFormat |
(date, tier) => string |
auto | Custom crosshair label formatter; tier is the active header tier. |
barLabel |
BarLabelOptions |
{ position: 'right' } |
Per-task bar label: position 'right'|'inside'|'left'|'auto', field, render, className, leadingPadding. |
columnLines |
boolean |
true |
Draw vertical lines between timeline columns. false keeps only row dividers. |
baseline |
{ enabled, color } |
disabled | See §2. |
parsing |
ParsingConfig |
— | Field-mapping to avoid manual transforms. |
toolbarItems |
ToolbarItem[] |
[] |
Custom toolbar buttons / selects / separators. |
tooltipTemplate |
(task, fmt) => string |
built-in | HTML string returned per task. |
annotations |
Annotation[] |
[] |
Vertical/horizontal markers on the timeline. |
Family theme tokens (--apx-*) (3.18.0+)
ApexGantt reads the shared ApexCharts family CSS tokens directly from the cascade, so a page can declare its brand once on :root and have the Gantt follow alongside every other Apex product on the page:
| Token | Role |
|---|---|
--apx-accent |
The color that means interactive or selected |
--apx-fore |
Text and anything that must stay legible on the surface |
--apx-grid |
Hairlines: borders, gridlines, connectors |
--apx-surface |
The plane content sits on |
--apx-series-1 … --apx-series-N |
Ordered categorical palette (1-based, stops at the first gap) |
:root {
--apx-accent: #5b21b6;
--apx-fore: #101828;
--apx-grid: #e4e7ec;
--apx-surface: #ffffff;
}
@media (prefers-color-scheme: dark) {
:root {
--apx-fore: #f8fafc;
--apx-grid: #334155;
--apx-surface: #0f172a;
}
}
Resolution order (highest wins):
product CSS variable > explicit option > --apx-* token > built-in default
Tokens resolve below anything you configured explicitly, so adopting them cannot change a chart that was already themed. One subtlety: an option set to a value equal to its built-in default is indistinguishable from one left alone, and the token wins there. Pin a product CSS variable if a value must hold regardless.
ApexGantt has no theme-name option, so the named-theme registry (registerTheme from @apex/commons) does not apply to it. Theme the Gantt via the CSS --apx-* tokens (optionally under a prefers-color-scheme block, as above) to keep it in sync with the rest of the family.
4. Lifecycle Pattern
import { ApexGantt } from 'apexgantt';
const gantt = new ApexGantt(document.getElementById('chart'), {
series: tasks,
pixelsPerDay: 25.7, // ≈ week density; omit to auto-fit
inputDateFormat: 'YYYY-MM-DD',
});
gantt.render(); // REQUIRED — paint to DOM
gantt.update({ pixelsPerDay: 80 }); // merge new options + re-render (80 = day density)
gantt.updateTask('t1', { progress: 75 }); // surgical single-task patch
gantt.zoomIn(); // raise pixelsPerDay (toward day-level detail)
gantt.zoomOut(); // lower pixelsPerDay (toward year overview)
gantt.destroy(); // free observers + DOM
update() preserves scroll position, collapsed/expanded state, and current zoom (pixelsPerDay) unless you explicitly override them.
5. Public API
Instance methods
| Method | Description |
|---|---|
render(_data?) |
Paints the chart. Returns the container HTMLElement. Call once after construction. |
update(options) |
Merge new options & re-render. Unspecified keys keep current values. |
updateTask(id, patch) |
Surgical single-task update. Throws if id not found. |
addTask(input, { parentId? }) |
Insert a task (recorded in history). Returns the Task, or null if beforeTaskAdd vetoed. Throws on duplicate id. |
deleteTask(id, { cascade? }) |
Remove a task. cascade: 'forbid' (default, throws if it has children), 'children', 'orphan'. Returns false if beforeTaskDelete vetoed. |
moveTask(id, { newParentId? }) |
Re-parent (null/omit → root). Returns false if beforeTaskMove vetoed. Throws on cycle / missing id. |
addDependency(fromId, toId, { type?, lag? }) |
Create an edge. Returns false if beforeDependencyChange vetoed. Throws on missing task / duplicate. |
removeDependency(fromId, toId) |
Remove an edge. Returns false if vetoed. Throws when no edge exists. |
canAddDependency(fromId, toId, opts?) |
Dry-run check. Returns { ok: true } or { ok: false, reason } ('self'/'task-missing'/'duplicate'/'cycle'/'summary-descendant'/'hook-veto'). |
undo() / redo() |
Traverse the history stack. No-op (returns false) when the stack is empty or history.enabled is false. |
canUndo() / canRedo() |
Whether a transaction is available to undo / redo. Gate toolbar buttons off these. |
clearHistory() |
Empty both stacks. Fires historyChange with kind: 'clear'. |
getHistorySize() |
{ undo, redo } stack sizes. |
zoomIn() / zoomOut() |
Step through Day → Week → Month → Quarter → Year. |
getSelectedTasks() |
Array of selected Task objects. Requires enableSelection: true. |
setSelectedTasks(ids) |
Replace selection by id array. |
clearSelection() |
Clear selection state. |
sort(criteria) |
Apply a sort (single or multi SortCriterion; [] clears). Hierarchy-preserving. Emits sortChange. |
clearSort() / getSort() |
Clear the sort (natural order) / read the active SortCriterion[]. |
toggleSort(key, { append? }) |
Header-click cycle asc → desc → none. append: true (Shift+click) adds a secondary key. |
filter(predicate) |
Apply a view-only predicate filter (keeps matches + their ancestors). Emits filterChange. |
setFilterRules(ruleSet) |
Apply a structured FilterRuleSet (pass null/empty to clear). Emits filterChange. |
clearFilter() / isFiltered() / getFilterRules() |
Clear the filter / query filter state / read the active rules (or null). |
groupBy(criterion) |
Group by a ColumnKey/string or GroupCriterion. Suspends the tree. Emits groupChange. |
clearGrouping() / getGroupBy() / isGrouping() |
Restore the tree / read the active GroupCriterion (or null) / query grouping state. |
setColumnWidth(key, px) |
Pin a column to an exact pixel width. Emits columnResize. |
resetColumnWidths(key?) / getColumnWidths() |
Clear one (or all) manual widths / read them as { key: px }. |
setColumnOrder(keys) / getColumnOrder() |
Set / read the left-to-right column order. Emits columnReorder. |
splitTask(id, at, { resumeAt? }) |
Split a task into worked segments at at (routes through updateTask; undoable). No-op on milestones/summaries. |
isSplit(id) |
Whether a task currently has multiple segments. |
getState() |
Capture the UI view state as a serializable GanttUiState. |
setState(state, { silent? }) |
Restore a (partial) GanttUiState. Re-renders once; emits sortChange/filterChange unless silent. |
scrollToTask(id) |
Scroll a task's bar into view (nearest-edge). Returns false if unknown / already visible. |
exportChart(format?) |
Export as 'svg'/'png'/'pdf' and trigger a download. Defaults to exportFormat. Returns a Promise. |
renderToolbar(container) |
Render the built-in toolbar into a custom DOM slot. |
destroy() |
Tear down. Required before unmounting in SPA frameworks. |
isDestroyed() |
Guard before calling other methods. |
Static method
| Method | Description |
|---|---|
ApexGantt.setLicense(key) |
Set the global license key once at app startup. Without it the chart renders with a watermark. |
6. Events
Events are dispatched as CustomEvents on the container element, not on the chart instance. Use the GanttEvents constants (or the string event names) to subscribe.
import { ApexGantt, GanttEvents } from 'apexgantt';
const container = document.getElementById('chart');
const gantt = new ApexGantt(container, { series: tasks });
gantt.render();
container.addEventListener(GanttEvents.TASK_UPDATE_SUCCESS, (e) => {
const { taskId, updatedTask, timestamp } = e.detail;
// persist to backend, update local state, etc.
});
container.addEventListener(GanttEvents.TASK_DRAGGED, (e) => {
const { taskId, newStartTime, newEndTime, daysMoved, affectedChildTasks } = e.detail;
});
| Constant | Event name | When |
|---|---|---|
GanttEvents.TASK_UPDATE |
taskUpdate |
Update submitted (before validation). |
GanttEvents.TASK_VALIDATION_ERROR |
taskValidationError |
Inline-edit form failed validation. |
GanttEvents.TASK_UPDATE_SUCCESS |
taskUpdateSuccess |
Update completed without error. |
GanttEvents.TASK_UPDATE_ERROR |
taskUpdateError |
Update threw. |
GanttEvents.TASK_DRAGGED |
taskDragged |
Bar dropped at a new position. |
GanttEvents.TASK_RESIZED |
taskResized |
Bar resized via a handle. |
GanttEvents.TASK_PROGRESS_CHANGED |
taskProgressChanged |
In-bar progress handle dragged. |
GanttEvents.TASK_ADDED |
taskAdded |
Task inserted via addTask() / toolbar / context menu. |
GanttEvents.TASK_DELETED |
taskDeleted |
Task removed via deleteTask(). One event per removed task on cascade. |
GanttEvents.TASK_MOVED |
taskMoved |
Task re-parented via moveTask(). |
GanttEvents.DEPENDENCY_ADDED |
dependencyAdded |
Edge created via addDependency(). |
GanttEvents.DEPENDENCY_REMOVED |
dependencyRemoved |
Edge removed via removeDependency(). |
GanttEvents.SELECTION_CHANGE |
selectionChange |
Selection set changed. |
GanttEvents.DEPENDENCY_ARROW_UPDATE |
dependencyArrowUpdate |
Internal arrow-redraw signal (task moved). Not a CRUD event. |
GanttEvents.HISTORY_CHANGE |
historyChange |
Undo/redo stack changed (kind: 'record'/'undo'/'redo'/'clear'). |
GanttEvents.SORT_CHANGE |
sortChange |
Sort changed. detail: { criteria: { key, direction }[], timestamp }. |
GanttEvents.FILTER_CHANGE |
filterChange |
Filter changed. detail: { active, visibleCount, timestamp }. |
GanttEvents.GROUP_CHANGE |
groupChange |
Grouping changed. detail: { active, field, groupCount, timestamp }. |
GanttEvents.COLUMN_RESIZE |
columnResize |
Column width changed. detail: { key, width, widths, timestamp } (width is null on reset). |
GanttEvents.COLUMN_REORDER |
columnReorder |
Column order changed. detail: { order, movedKey, timestamp } (movedKey is null for bulk setColumnOrder). |
7. Pitfalls & Anti-Patterns
Pitfall 1: Date format mismatch
❌ WRONG — passing ISO dates without setting the format:
new ApexGantt(el, { series: [{ id: 't1', name: 'X', startTime: '2026-06-01', endTime: '2026-06-15' }] });
// Bars won't render — default inputDateFormat is 'MM-DD-YYYY'.
✅ CORRECT:
new ApexGantt(el, {
inputDateFormat: 'YYYY-MM-DD',
series: [{ id: 't1', name: 'X', startTime: '2026-06-01', endTime: '2026-06-15' }]
});
Pitfall 2: Missing render()
❌ new ApexGantt(el, opts) alone — nothing paints.
✅ const g = new ApexGantt(el, opts); g.render();
Pitfall 3: Re-creating without destroying (React/Vue leak)
❌ WRONG:
useEffect(() => {
const g = new ApexGantt(ref.current, { series: tasks });
g.render();
}, [tasks]); // ResizeObservers + listeners pile up on every change
✅ CORRECT:
useEffect(() => {
const g = new ApexGantt(ref.current, { series: tasks });
g.render();
return () => g.destroy();
}, [tasks]);
Pitfall 4: Using update() to push a single progress change
❌ Slow — re-layouts the whole timeline:
gantt.update({ series: tasks.map(t => t.id === 't3' ? { ...t, progress: 75 } : t) });
✅ Fast — patches only the affected bar:
gantt.updateTask('t3', { progress: 75 });
Pitfall 5: Milestone with endTime
❌ WRONG — milestone with a duration is rendered as a regular bar:
{ id: 'm1', name: 'Launch', type: 'milestone', startTime: '02-20-2026', endTime: '02-25-2026' }
✅ CORRECT — omit endTime:
{ id: 'm1', name: 'Launch', type: 'milestone', startTime: '02-20-2026' }
Pitfall 6: Listening for events on the gantt instance
❌ WRONG — gantt is not an EventTarget:
gantt.addEventListener('taskDragged', handler); // TypeError
✅ CORRECT — listen on the container element:
container.addEventListener(GanttEvents.TASK_DRAGGED, handler);
Pitfall 7: Forgetting enableSelection
❌ — selection APIs return empty / no-op:
new ApexGantt(el, { series: tasks });
gantt.getSelectedTasks(); // []
✅ — opt in:
new ApexGantt(el, { series: tasks, enableSelection: true });
Pitfall 8: progress as a 0–1 fraction
❌ { ..., progress: 0.75 } renders a 1% bar.
✅ { ..., progress: 75 }.
Pitfall 9: Orphan parentId
❌ parentId: 'phase-x' when no task with id 'phase-x' exists — the row is silently flattened to top level.
✅ Make sure every parentId refers to a task in the same series array.
Pitfall 10: Forgetting to set the license key
❌ — chart renders a watermark. ✅ — call once at app startup, before constructing any chart:
import { ApexGantt } from 'apexgantt';
ApexGantt.setLicense('YOUR_LICENSE_KEY');
License signature verification (3.17.0+): license keys issued since 2026-07-27 are ECDSA-signature-verified at runtime; a tampered key renders the chart with a watermark. Unsigned (older) keys remain valid until 2027-07-31. Use the exact key string ApexCharts issued you, verbatim.
Pitfall 11: Mutating series in place
❌ — direct mutation doesn't notify the chart:
options.series.push(newTask); // chart still shows old data
✅ — go through the API:
gantt.update({ series: [...options.series, newTask] });
Pitfall 12: Custom toolbar callback expects this
❌ Toolbar onClick receives ToolbarContext, not the gantt instance.
✅ Capture the instance in closure if you need it:
const gantt = new ApexGantt(el, {
toolbarItems: [{
type: 'button',
label: 'Save',
requiresSelection: true,
onClick: ({ selectedTasks }) => save(selectedTasks), // use closure for gantt
}],
enableSelection: true,
series: tasks,
});
8. Data Parsing (non-standard shapes)
Skip manual .map() transforms — pass parsing and let ApexGantt read your fields directly. Supports dot-notation and per-field transform functions.
const apiData = [
{ task_id: 'T1', task_name: 'Design', start_date: '01-01-2026', end_date: '01-15-2026', completion: 0.75 }
];
new ApexGantt(el, {
series: apiData,
parsing: {
id: 'task_id',
name: 'task_name',
startTime: 'start_date',
endTime: 'end_date',
progress: { key: 'completion', transform: (v) => v * 100 }, // 0–1 → 0–100
},
});
Supported parse keys: id, name, startTime, endTime, progress, type, parentId, dependency, barBackgroundColor, rowBackgroundColor, collapsed.
9. Working Calendar & Sub-Day Scheduling
calendar (CalendarOptions)
When set, weekends + holidays drive duration math, summary aggregation, and timeline stripes. Absent = every day is a working day.
new ApexGantt(el, {
series: tasks,
calendar: {
workingWeekdays: [1, 2, 3, 4, 5], // 0=Sun … 6=Sat; default Mon–Fri
holidays: ['12-25-2026', { date: '01-01-2027', label: 'New Year' }],
showNonWorkingStripes: true, // hatched bands over non-working columns; default true
holidayTooltip: ({ date, label }) => `<b>${label ?? 'Holiday'}</b>`,
dragSnapMode: 'next', // 'next' (default) | 'previous' | 'allow'
},
});
dragSnapMode controls what happens when a drag/resize lands on a non-working day: 'next'/'previous' snap to the adjacent working day (drag preserves working-day duration), 'allow' permits it. TaskDependency.lagUnit defaults to 'working' when a calendar is set — lag counts working days only; set 'calendar' to force raw calendar days.
snapUnit / snapValue (sub-day)
Granularity that drag/resize/inline edits snap to — independent of the header tier (which follows pixelsPerDay).
new ApexGantt(el, {
inputDateFormat: 'YYYY-MM-DD HH:mm', // include time tokens for sub-day work
snapUnit: 'minute',
snapValue: 15, // 15-minute increments
series: tasks,
});
With 'hour'/'minute', endTime is treated as the exclusive end timestamp and taskDragged.daysMoved becomes fractional (a 6-hour move reports 0.25). 'day' (default) treats endTime as inclusive.
10. Editing: CRUD, Undo/Redo & Interaction Toggles
Programmatic CRUD, all recorded in the undo history, all firing events and passing through optional veto hooks. See references/editing.md for full detail.
gantt.addTask({ id: 't9', name: 'Review', startTime: '08-01-2026', endTime: '08-05-2026' });
gantt.addTask({ id: 'subA', name: 'Sub', startTime: '08-01-2026', endTime: '08-03-2026' }, { parentId: 't9' });
gantt.deleteTask('t9', { cascade: 'children' }); // 'forbid' (default) | 'children' | 'orphan'
gantt.moveTask('subA', { newParentId: null }); // re-parent to root
gantt.addDependency('t1', 't2', { type: 'FS', lag: 2 });
if (gantt.canAddDependency('t1', 't2').ok) { /* … */ }
gantt.removeDependency('t1', 't2');
Undo/redo — every mutating call (drag, resize, progress, edit, add/delete/move, dependency change) is recorded unless history: { enabled: false }. Ctrl/Cmd+Z / Ctrl+Y work inside the chart automatically.
gantt.undo(); gantt.redo();
if (gantt.canUndo()) { /* enable Undo button */ }
container.addEventListener(GanttEvents.HISTORY_CHANGE, (e) => {
const { kind, canUndo, canRedo } = e.detail; // keep external buttons in sync
});
Veto hooks — synchronous; return false to cancel: beforeTaskAdd, beforeTaskUpdate, beforeTaskMove, beforeTaskDelete, beforeDependencyChange.
Interaction toggles expose the same operations to the user: enableInlineEdit, enableProgressDrag, enableTaskEditingShortcuts, enableTaskCRUDToolbar, enableContextMenu, enableAddTaskRow (see §3).
11. Reference Routing Table
For deeper detail and full working examples, refer to:
| Topic | Reference File |
|---|---|
| Task data, hierarchy, milestones, baseline, assignees, summary bars, split tasks | references/data-format.md |
Dependencies (FS/SS/FF/SF), lag units, critical path |
references/dependencies.md |
Column config, ProgressRing / Wbs, built-in column keys, renderers, custom toolbar items, parsing |
references/columns-and-toolbar.md |
| Task-list grid: sorting, filtering, grouping, auto-size / resize / reorder columns | references/grid.md |
| UI state persistence, draw-to-create, scroll-to-task, export (SVG/PNG/PDF) | references/interaction.md |
| Events, selection, inline edit, drag/resize, progress drag | references/events.md |
| CRUD API, undo/redo, calendar, sub-day scheduling, interaction toggles | references/editing.md |
| React, Vue 3, Angular wrappers | references/framework-wrappers.md |