React ApexGantt
Installation
npm install react-apexgantt apexgantt
License Setup
Call setApexGanttLicense once at app startup, before any component renders. The best place is your entry file:
// main.tsx
import React from "react"
import ReactDOM from "react-dom/client"
import { setApexGanttLicense } from "react-apexgantt"
import App from "./App"
setApexGanttLicense("your-license-key-here")
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
Quick Start
import { ApexGanttChart } from "react-apexgantt"
import type { TaskInput } from "react-apexgantt"
const tasks: TaskInput[] = [
{
id: "phase-1",
name: "Phase 1: Research",
startTime: "2024-03-01",
endTime: "2024-03-15",
progress: 100,
},
{
id: "task-1",
name: "User interviews",
startTime: "2024-03-01",
endTime: "2024-03-08",
parentId: "phase-1",
progress: 100,
},
{
id: "task-2",
name: "Competitive analysis",
startTime: "2024-03-06",
endTime: "2024-03-15",
parentId: "phase-1",
progress: 80,
},
{
id: "phase-2",
name: "Phase 2: Design",
startTime: "2024-03-16",
endTime: "2024-04-05",
progress: 30,
dependency: "phase-1",
},
]
export default function App() {
return <ApexGanttChart tasks={tasks} viewMode="week" height="500px" />
}
parentId links a task to its parent row to form a hierarchy. dependency draws an arrow between tasks and represents a finish-to-start dependency.
Props
| Prop | Type | Description |
|---|---|---|
tasks | TaskInput[] | Array of tasks to render |
options | Omit<GanttUserOptions, 'series'> | Full chart configuration (excludes series, which is derived from tasks) |
width | string | number | Chart width — overrides options.width |
height | string | number | Chart height — overrides options.height |
viewMode | ViewMode | Time-scale: 'day', 'week', 'month', 'quarter', 'year' |
theme | 'light' | 'dark' | Color theme |
className | string | CSS class on the wrapper <div> |
style | CSSProperties | Inline styles on the wrapper <div> |
Events
All event callbacks receive a typed detail object. The component stores handlers in a ref internally, so changing the callback on re-render does not require wrapping it in useCallback.
| Prop | Fires when |
|---|---|
onTaskUpdate | A task edit is in progress (before completion) |
onTaskUpdateSuccess | A task update commits successfully |
onTaskUpdateError | A task update fails |
onTaskValidationError | Task form validation fails |
onTaskDragged | A task bar is dragged to a new date |
onTaskResized | A task bar is resized via its handles |
onSelectionChange | The set of selected tasks changes |
onDependencyArrowUpdate | A dependency arrow is created, moved, or deleted |
import { ApexGanttChart } from "react-apexgantt"
export default function GanttWithEvents() {
return (
<ApexGanttChart
tasks={tasks}
height="500px"
onTaskUpdateSuccess={(detail) => {
console.log("Task updated:", detail)
// save to your backend
}}
onTaskDragged={(detail) => {
console.log("Task dragged:", detail)
}}
onTaskUpdateError={(detail) => {
console.error("Update failed:", detail.error)
}}
/>
)
}
Imperative API via ref
Use useRef<ApexGanttHandle> to call chart methods programmatically after mount.
import { useRef } from "react"
import { ApexGanttChart } from "react-apexgantt"
import type { ApexGanttHandle } from "react-apexgantt"
export default function GanttWithControls() {
const ganttRef = useRef<ApexGanttHandle>(null)
return (
<div>
<div style={{ marginBottom: 12, display: "flex", gap: 8 }}>
<button onClick={() => ganttRef.current?.zoomIn()}>Zoom In</button>
<button onClick={() => ganttRef.current?.zoomOut()}>Zoom Out</button>
<button onClick={() => {
ganttRef.current?.updateTask("task-1", { progress: 100 })
}}>
Mark task-1 done
</button>
</div>
<ApexGanttChart ref={ganttRef} tasks={tasks} height="500px" />
</div>
)
}
Available methods on ApexGanttHandle:
| Method | Description |
|---|---|
update(options) | Replace the full chart configuration |
updateTask(taskId, data) | Update a single task by ID with partial fields |
zoomIn() | Zoom in one time-scale level |
zoomOut() | Zoom out one time-scale level |
destroy() | Destroy the chart and clean up DOM and listeners |
getInstance() | Return the underlying raw ApexGantt instance |
Dynamic view mode and theme
Bind viewMode and theme to React state. The component diffs props on every render and calls update() internally only when something changes.
import { useState } from "react"
import { ApexGanttChart } from "react-apexgantt"
import type { ViewMode } from "react-apexgantt"
export default function DynamicGantt() {
const [viewMode, setViewMode] = useState<ViewMode>("week")
const [theme, setTheme] = useState<"light" | "dark">("light")
return (
<div>
<div style={{ marginBottom: 12, display: "flex", gap: 12 }}>
<label>
View:
<select
value={viewMode}
onChange={(e) => setViewMode(e.target.value as ViewMode)}
>
<option value="day">Day</option>
<option value="week">Week</option>
<option value="month">Month</option>
<option value="quarter">Quarter</option>
<option value="year">Year</option>
</select>
</label>
<button onClick={() => setTheme(t => t === "light" ? "dark" : "light")}>
Toggle theme
</button>
</div>
<ApexGanttChart
tasks={tasks}
viewMode={viewMode}
theme={theme}
height="500px"
/>
</div>
)
}
Options
Pass any configuration from the options reference via the options prop. The series key is omitted — it is always derived from tasks.
import type { GanttUserOptions } from "react-apexgantt"
const options: Omit<GanttUserOptions, "series"> = {
enableTaskDrag: true,
enableTaskResize: true,
enableTaskEdit: true,
enableInlineEdit: true,
barBackgroundColor: "#537CFA",
rowHeight: 36,
columnConfig: [
{ key: "name", title: "Task", minWidth: "160px", flexGrow: 3 },
{ key: "startTime", title: "Start", minWidth: "100px", flexGrow: 1 },
{ key: "endTime", title: "End", minWidth: "100px", flexGrow: 1 },
{ key: "progress", title: "Progress", minWidth: "80px", flexGrow: 1 },
],
}
export default function ConfiguredGantt() {
return <ApexGanttChart tasks={tasks} options={options} height="600px" />
}
useGanttData — parsing external data
When your API returns data in a different shape, useGanttData maps and memoizes it into the TaskInput[] format ApexGantt expects. It only re-parses when data or parsing changes.
import { useGanttData, ApexGanttChart } from "react-apexgantt"
const rawApiData = [
{
task_id: "T1",
task_name: "Planning Phase",
start: "2024-01-01",
end: "2024-01-10",
completion: 100,
},
{
task_id: "T2",
task_name: "Development",
start: "2024-01-11",
end: "2024-01-25",
completion: 60,
depends_on: "T1",
},
{
task_id: "T3",
task_name: "Frontend",
start: "2024-01-11",
end: "2024-01-18",
completion: 80,
parent_task: "T2",
},
]
const parsingConfig = {
id: "task_id",
name: "task_name",
startTime: "start",
endTime: "end",
progress: "completion",
dependency: "depends_on",
parentId: "parent_task",
}
export default function ParsedGantt() {
const tasks = useGanttData({ data: rawApiData, parsing: parsingConfig })
return <ApexGanttChart tasks={tasks} height="500px" />
}
For nested objects, use dot-notation paths. For values that need transformation, pass an object with key and transform:
const parsingConfig = {
id: "project.task.id",
name: "project.task.title",
startTime: "project.dates.start",
endTime: "project.dates.end",
progress: {
key: "project.status.completion",
transform: (value: number) => value * 100, // convert 0-1 to 0-100
},
}
useGanttEvents — memoized event handlers
When you need stable event handler references (for example, inside useCallback or when passing to child components), use useGanttEvents:
import { useGanttEvents, ApexGanttChart } from "react-apexgantt"
export default function GanttWithMemoizedEvents() {
const { handleTaskUpdate, handleTaskDragged, handleTaskResized } =
useGanttEvents({
onTaskUpdate: (detail) => console.log("updating:", detail),
onTaskDragged: (detail) => console.log("dragged:", detail),
onTaskResized: (detail) => console.log("resized:", detail),
})
return (
<ApexGanttChart
tasks={tasks}
height="500px"
onTaskUpdate={handleTaskUpdate}
onTaskDragged={handleTaskDragged}
onTaskResized={handleTaskResized}
/>
)
}
TypeScript
All types are exported from react-apexgantt:
import type {
TaskInput,
ViewMode,
ThemeMode,
GanttUserOptions,
ApexGanttHandle,
ApexGanttChartProps,
} from "react-apexgantt"
For event detail types, import from the underlying apexgantt package:
import type { GanttEventMap } from "apexgantt"
type TaskUpdateDetail = GanttEventMap["taskUpdateSuccess"]["detail"]