Inline Editing

ApexGantt provides several layers of interactive editing. Each layer is opt-in and can be combined independently.

Editing modes

OptionWhat it enables
enableTaskDragDrag task bars horizontally to reschedule
enableTaskResizeDrag the left or right handle of a bar to change start or end date
enableProgressDragDrag the small handle at the bar's bottom edge to set completion percent
enableTaskEditClick a task bar to open a dialog form (name, dates, progress)
enableInlineEditDouble-click a cell in the task list to edit it in place
enableTaskCRUDToolbarShow Add and Delete buttons in the toolbar
enableContextMenuRight-click a bar or row for a contextual action menu
enableAddTaskRowShow a "+ Add task" row at the bottom of the task list
enableTaskEditingShortcutsDelete/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:

  • Delete or Backspace — 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
})
ExamplesnapUnitsnapValue
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)
      }}
    />
  )
}