Imperative API

Drive the chart from outside via apiRef: create, update, undo, redo, scroll, zoom.

demo.tsx
"use client";

import { useCallback, useRef, useState } from "react";
import { Gantt, type GanttHandle, type GanttTask } from "@art-tools/react-gantt";
import { simpleTasks } from "@/lib/demo-tasks";

/**
 * `apiRef` exposes the same operations the chart uses internally, so a toolbar
 * outside it can drive the chart without lifting its state.
 *
 * Every mutation goes through the change log, so undo/redo covers them all —
 * including the cascade that `updateTask` triggers on dependents.
 */
export function ImperativeApiDemo() {
  const api = useRef<GanttHandle>(null);
  const [tasks, setTasks] = useState<GanttTask[]>(simpleTasks);
  const [n, setN] = useState(0);

  const addTask = useCallback(() => {
    const id = `added-${n}`;
    setN((v) => v + 1);
    // createTask commits synchronously, then selects and scrolls to the new row.
    api.current?.createTask({
      id,
      name: `New task ${n + 1}`,
      startDate: new Date(2026, 0, 6 + n),
      endDate: new Date(2026, 0, 9 + n),
      progress: 0,
      type: "task",
      parentId: null,
    });
  }, [n]);

  return (
    <div>
      <div
        style={{
          display: "flex",
          flexWrap: "wrap",
          gap: 8,
          padding: "8px 12px",
          borderBottom: "1px solid #e2e8f0",
        }}
      >
        <button type="button" onClick={addTask}>
          Add task
        </button>
        <button type="button" onClick={() => api.current?.undo()}>
          Undo
        </button>
        <button type="button" onClick={() => api.current?.redo()}>
          Redo
        </button>
        <button
          type="button"
          // `horizontal: true` because this chart fits vertically — the interesting
          // axis here is the timeline. A bare `revealTask(id)` scrolls the rows.
          onClick={() => api.current?.revealTask("ship", { horizontal: true })}
        >
          Scroll to “Ship it”
        </button>
        <button type="button" onClick={() => api.current?.updateTask("build", { progress: 100 })}>
          Complete “Build feature”
        </button>
        <button type="button" onClick={() => api.current?.zoomIn()}>
          Zoom in
        </button>
        <button type="button" onClick={() => api.current?.zoomOut()}>
          Zoom out
        </button>
      </div>
      <Gantt apiRef={api} tasks={tasks} onTasksChange={setTasks} height={320} />
    </div>
  );
}