React ApexGantt

Installation

npm install react-apexgantt apexgantt

View Demo Project

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

PropTypeDescription
tasksTaskInput[]Array of tasks to render
optionsOmit<GanttUserOptions, 'series'>Full chart configuration (excludes series, which is derived from tasks)
widthstring | numberChart width — overrides options.width
heightstring | numberChart height — overrides options.height
viewModeViewModeTime-scale: 'day', 'week', 'month', 'quarter', 'year'
theme'light' | 'dark'Color theme
classNamestringCSS class on the wrapper <div>
styleCSSPropertiesInline 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.

PropFires when
onTaskUpdateA task edit is in progress (before completion)
onTaskUpdateSuccessA task update commits successfully
onTaskUpdateErrorA task update fails
onTaskValidationErrorTask form validation fails
onTaskDraggedA task bar is dragged to a new date
onTaskResizedA task bar is resized via its handles
onSelectionChangeThe set of selected tasks changes
onDependencyArrowUpdateA 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:

MethodDescription
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"]