Skip to main content
The Gantt fires an event for every action that occurs, whether triggered by the user or by api.exec(). You can listen to events to react to changes, or intercept them to validate and cancel operations.

Listening to events with api.on()

api.on() registers a callback that fires after an action has been applied to the state. Use it to react to changes, sync to a backend, or update external UI.
function init(api) {
  api.on("add-task", ({ id, task }) => {
    console.log("Task created:", id, task.text);
  });

  api.on("update-task", ({ id, task }) => {
    console.log("Task updated:", id);
  });

  api.on("delete-task", ({ id }) => {
    console.log("Task deleted:", id);
  });
}
You can also handle events directly as component props using the on<ActionName> naming convention:
<Gantt
  {tasks}
  {links}
  onaddtask={({ id, task }) => console.log("Added:", id)}
  onupdatetask={({ id }) => console.log("Updated:", id)}
  ondeletetask={({ id }) => console.log("Deleted:", id)}
/>
Event prop names are derived by removing hyphens from the action name and prepending on. For example, "add-task" becomes onaddtask.

Intercepting events with api.intercept()

api.intercept() registers a callback that fires before the action is processed. Return false from the callback to cancel the action. Any other return value (including undefined) allows the action to proceed.
function init(api) {
  // Prevent deleting tasks that still have subtasks
  api.intercept("delete-task", ({ id }) => {
    const task = api.getTask(id);
    if (task.data?.length) {
      alert("Remove subtasks before deleting this task.");
      return false; // cancels the deletion
    }
  });
}

Difference between on() and intercept()

api.on()api.intercept()
When it firesAfter the action is appliedBefore the action is applied
Can cancel the actionNoYes, by returning false
Use caseReacting to changes, syncing to backendValidating, guarding, or blocking changes

Cancelling an action

Return false from an intercept callback to cancel the action. The state is not modified and no on() callbacks fire.
function init(api) {
  let allowAdd = true;

  // Dynamically block or allow adding tasks
  api.intercept("add-task", () => {
    if (!allowAdd) return false;
  });

  // Block drag-reordering but allow horizontal drag
  api.intercept("drag-task", (ev) => {
    if (typeof ev.top !== "undefined") return false; // block vertical reorder
  });

  // Block creating new links
  api.intercept("add-link", () => false);
}

Grouping and removing listeners

Pass a tag to group related listeners. Call api.detach(tag) to remove all listeners in the group at once.
const tag = Symbol("ganttListeners");

function init(api) {
  api.on("add-task", onAddTask, { tag });
  api.on("update-task", onUpdateTask, { tag });
  api.intercept("delete-task", guard, { tag });
}

// Later, remove all listeners at once
function cleanup() {
  api.detach(tag);
}

Available events

Every action listed in Actions is also an event. The same parameters are passed to the callback. Below is a quick reference.

Task events

EventWhen it firesKey params
add-taskAfter a task is addedid, task, target, mode
update-taskAfter a task is updatedid, task
delete-taskAfter a task is deletedid, source (parent ID)
copy-taskAfter a task is copiedid (new), source (original)
move-taskAfter a task is movedid, target, mode, source
indent-taskAfter indent/outdentid, mode
open-taskAfter expand/collapseid, mode
select-taskAfter selection changesid, toggle, range
drag-taskDuring dragid, left, top, width, inProgress
EventWhen it firesKey params
add-linkAfter a link is addedid, link
update-linkAfter a link is updatedid, link
delete-linkAfter a link is deletedid

View events

EventWhen it firesKey params
scroll-chartAfter the chart scrollsleft, top, date
resize-chartAfter the chart resizeswidth, height
sort-tasksAfter tasks are sortedkey, order
filter-tasksAfter tasks are filteredfilter, key, value
zoom-scaleAfter zoom changesdir, ratio, offset

PRO events

EventWhen it firesKey params
undoAfter an undo
redoAfter a redo
export-dataWhen export is triggeredformat, fileName, …

Full example

<script>
  import { Gantt } from "@svar-ui/svelte-gantt";

  let tasks = $state([...]);
  let links = $state([...]);

  function init(api) {
    // Log all task changes
    api.on("add-task",    ({ id, task }) => saveToServer("POST",   task));
    api.on("update-task", ({ id, task }) => saveToServer("PUT",    task));
    api.on("delete-task", ({ id })       => saveToServer("DELETE", { id }));

    // Guard: require task text before adding
    api.intercept("add-task", ({ task }) => {
      if (!task.text?.trim()) {
        alert("Task name is required.");
        return false;
      }
    });

    // Guard: prevent all link creation
    api.intercept("add-link", () => false);
  }

  function saveToServer(method, data) {
    fetch("/api/tasks", { method, body: JSON.stringify(data) });
  }
</script>

<Gantt {tasks} {links} {init} />

Build docs developers (and LLMs) love