Inline Editing
ApexGantt provides several layers of interactive editing. Each layer is opt-in and can be combined independently.
Editing modes
| Option | What it enables |
|---|---|
enableTaskDrag | Drag task bars horizontally to reschedule |
enableTaskResize | Drag the left or right handle of a bar to change start or end date |
enableProgressDrag | Drag the small handle at the bar's bottom edge to set completion percent |
enableTaskEdit | Click a task bar to open a dialog form (name, dates, progress) |
enableInlineEdit | Double-click a cell in the task list to edit it in place |
enableTaskCRUDToolbar | Show Add and Delete buttons in the toolbar |
enableContextMenu | Right-click a bar or row for a contextual action menu |
enableAddTaskRow | Show a "+ Add task" row at the bottom of the task list |
enableTaskEditingShortcuts | Delete/Backspace to delete focused row; Tab/Shift+Tab to indent/outdent |
All edits pass through the same command path, so they are all undoable via undo() and fire the same event stream (taskUpdate, taskUpdateSuccess, taskUpdateError, taskValidationError).
Minimal interactive setup
const gantt = new ApexGantt(document.getElementById('chart'), {
series: tasks,
enableTaskDrag: true,
enableTaskResize: true,
enableProgressDrag: true,
})
Dialog edit
Setting enableTaskEdit: true shows an edit form when a task bar is clicked. The form exposes name, start date, end date, and progress. Summary rows open a read-only view.
enableInlineEdit is auto-enabled when enableTaskEdit is true. To keep only the dialog without cell-level editing, opt out explicitly:
const gantt = new ApexGantt(el, {
series: tasks,
enableTaskEdit: true,
enableInlineEdit: false, // keep dialog, disable double-click cell edit
})
Inline cell editing
With enableInlineEdit: true, double-clicking a name, startTime, endTime, duration, or progress cell turns it into an input:
- Enter or blur commits the value
- Escape cancels and restores the original value
Summary rows, milestones (date/duration/progress cells), and empty rows are not editable inline.
const gantt = new ApexGantt(el, {
series: tasks,
enableInlineEdit: true,
})
CRUD toolbar
enableTaskCRUDToolbar: true adds + Add Task and a trash-icon Delete button to the toolbar. The delete button operates on the current selection (requires enableSelection: true). Undo/Redo buttons also appear automatically.
const gantt = new ApexGantt(el, {
series: tasks,
enableTaskCRUDToolbar: true,
enableSelection: true,
enableTaskEdit: true,
})
Context menu
enableContextMenu: true shows a right-click menu on task bars and task-list rows. Available entries depend on the task type and enabled options:
- Edit — requires
enableTaskEdit: true - Add child / Add sibling — inserts a new task in the hierarchy
- Indent / Outdent — available only when a valid sibling or parent exists
- Delete — deletes the task with
cascade: 'children'
const gantt = new ApexGantt(el, {
series: tasks,
enableContextMenu: true,
enableTaskEdit: true,
})
Add task row
enableAddTaskRow: true renders a + Add task button row below the last task. Clicking it inserts a new root-level placeholder and emits taskAdded. This affordance is disabled automatically when the dataset exceeds 50 rows — use the toolbar or context menu for large datasets.
const gantt = new ApexGantt(el, {
series: tasks,
enableAddTaskRow: true,
})
Keyboard shortcuts
enableTaskEditingShortcuts: true adds:
DeleteorBackspace— deletes the focused row (cascade:'children')Tab— indent (make the row a child of the row above it)Shift+Tab— outdent (promote the row to the parent's sibling)
The task list panel must have keyboard focus. Standard roving-tabindex navigation (ArrowUp, ArrowDown, Home, End) is always available regardless of this option.
const gantt = new ApexGantt(el, {
series: tasks,
enableTaskEditingShortcuts: true,
enableSelection: true,
})
Snapping drag and resize
By default, bars snap to whole-day boundaries. Use snapUnit and snapValue to snap to finer increments:
const gantt = new ApexGantt(el, {
series: tasks,
enableTaskDrag: true,
enableTaskResize: true,
snapUnit: 'hour', // 'day' | 'hour' | 'minute'
snapValue: 4, // snap to every 4 hours
})
| Example | snapUnit | snapValue |
|---|---|---|
| Whole day (default) | 'day' | 1 |
| Half day | 'hour' | 12 |
| 4-hour blocks | 'hour' | 4 |
| 15-minute slots | 'minute' | 15 |
| 30-minute slots | 'minute' | 30 |
Validating edits with beforeTaskUpdate
Use the beforeTaskUpdate hook to intercept and veto any edit before it commits — whether it came from drag, resize, inline edit, dialog, or a programmatic updateTask() call.
Return true to allow or false to reject:
const gantt = new ApexGantt(el, {
series: tasks,
enableTaskDrag: true,
enableTaskResize: true,
beforeTaskUpdate: (taskId, changes, currentTask) => {
// prevent moving tasks into the past
if (changes.startTime && new Date(changes.startTime) < new Date()) {
return false
}
// prevent progress going backwards
if (changes.progress !== undefined && changes.progress < currentTask.progress) {
return false
}
return true
},
})
When the hook returns false, a taskValidationError event fires and the chart reverts to its previous state. No historyChange event is emitted for vetoed edits.
Full interactive configuration example
const gantt = new ApexGantt(document.getElementById('chart'), {
series: tasks,
// drag and resize
enableTaskDrag: true,
enableTaskResize: true,
enableProgressDrag: true,
snapUnit: 'hour',
snapValue: 4,
// forms and cell editing
enableTaskEdit: true,
enableInlineEdit: true,
// affordances
enableTaskCRUDToolbar: true,
enableContextMenu: true,
enableSelection: true,
enableTaskEditingShortcuts: true,
// undo/redo
history: { enabled: true, maxSize: 50 },
// validation
beforeTaskUpdate: (taskId, changes) => {
// add your validation logic here
return true
},
})
// listen for outcomes
gantt.el.addEventListener('taskUpdateSuccess', (e) => {
console.log('Saved:', e.detail)
// persist to your backend
})
gantt.el.addEventListener('taskValidationError', (e) => {
console.warn('Rejected:', e.detail)
})
React example
import { ApexGanttChart } from 'react-apexgantt'
import type { GanttUserOptions } from 'react-apexgantt'
const options: Omit<GanttUserOptions, 'series'> = {
enableTaskDrag: true,
enableTaskResize: true,
enableProgressDrag: true,
enableTaskEdit: true,
enableInlineEdit: true,
enableContextMenu: true,
enableTaskCRUDToolbar: true,
enableSelection: true,
snapUnit: 'hour',
snapValue: 4,
beforeTaskUpdate: (taskId, changes, current) => {
if (changes.startTime && new Date(changes.startTime) < new Date()) {
return false
}
return true
},
}
export default function EditableGantt() {
return (
<ApexGanttChart
tasks={tasks}
options={options}
height="600px"
onTaskUpdateSuccess={(detail) => {
// persist to backend
console.log('Updated:', detail)
}}
onTaskValidationError={(detail) => {
console.warn('Validation failed:', detail)
}}
/>
)
}