Vue ApexGantt

Installation

npm install vue-apexgantt apexgantt
# or
yarn add vue-apexgantt apexgantt
# or
pnpm add vue-apexgantt apexgantt

apexgantt is a peer dependency. This page describes the vue-apexgantt release built for apexgantt 3.20.0, which requires apexgantt 3.20.0 or later; notes mark what differs on earlier versions.

Demo

View Demo Project created using Vue-ApexGantt

License Setup

ApexGantt is included in the Premium plan and above, and there is no free tier. Without a key the chart renders with a trial watermark. Set your license key once at application start:

import { setApexGanttLicense } from 'vue-apexgantt';

// call this at the top of your app
setApexGanttLicense('your-license-key-here');

Example with Vue app entry point:

// main.ts
import { createApp } from 'vue';
import { setApexGanttLicense } from 'vue-apexgantt';
import App from './App.vue';

// set license before rendering
setApexGanttLicense('your-license-key-here');

createApp(App).mount('#app');

A key set later still removes the watermark from charts already on the page (apexgantt 3.17.0 and later); setting it first avoids a one-frame watermark. From apexgantt 3.20.0 the key's plan must be Premium or higher.

Quick Start

<template>
  <ApexGanttChart
    :tasks="tasks"
    :pixels-per-day="25.7"
    height="500px"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { ApexGanttChart } from 'vue-apexgantt';
import type { TaskInput } from 'vue-apexgantt';

const tasks = ref<TaskInput[]>([
  {
    id: 'task-1',
    name: 'Project Planning',
    startTime: '01-01-2024',
    endTime: '01-08-2024',
    progress: 75,
  },
  {
    id: 'task-2',
    name: 'Development',
    startTime: '01-09-2024',
    endTime: '01-20-2024',
    progress: 40,
    dependency: 'task-1',
  },
]);
</script>

API Reference

Component Props

PropTypeDefaultDescription
tasksTaskInput[]RequiredTasks to display. A change replaces the chart's data (see Prop changes and edits below)
optionsAny other ApexGantt option. Keys set here win over the matching props
widthstring | number'100%'Chart width. A number is read as pixels
heightstring | numberNoneChart height. A number is read as pixels. Leave it out to size the chart from CSS: with apexgantt 3.20.0 the chart fills a container that has a height of its own, and is 500px tall otherwise. Earlier releases of the component defaulted it to '500px', which overrode a height set in CSS
pixelsPerDaynumberNoneTimeline zoom in pixels-per-day; header tier auto-picks (0.5 year, 1.6 quarter, 4.9 month, 25.7 week, 80 day). Leave it out and the chart fits its zoom to the data span on first render. It is sent to the chart only when it changes to a number, so a zoom made from the toolbar survives other prop changes. Earlier releases of the component defaulted it to 4.9, so the chart never auto-fitted
theme'light' | 'dark''light'Color theme
classNamestring''Class added to the chart container
styleCSSPropertiesInline styles added to the chart container

Component Events

The component re-emits every ApexGantt event under the core's name, with the event's detail as the only argument. In a template, listen with the kebab-case form. Every detail type is exported from vue-apexgantt. Earlier releases emitted eight of these events (taskUpdate, taskUpdateSuccess, taskValidationError, taskUpdateError, taskDragged, taskResized, selectionChange, dependencyArrowUpdate).

EventTemplate listenerPayload TypeFires when
taskUpdate@task-updateTaskUpdateEventDetailAn edit is being committed, before taskUpdateSuccess
taskUpdateSuccess@task-update-successTaskUpdateSuccessEventDetailAn edit was applied
taskUpdateError@task-update-errorTaskUpdateErrorEventDetailAn edit failed to apply
taskValidationError@task-validation-errorTaskValidationErrorEventDetailThe task edit dialog failed validation
taskAdded@task-addedTaskAddedEventDetailA task was inserted
taskDeleted@task-deletedTaskDeletedEventDetailA task was removed (once per task in a cascading delete)
taskMoved@task-movedTaskMovedEventDetailA task was moved to a new parent
dependencyAdded@dependency-addedDependencyChangeEventDetailA dependency link was created, by addDependency() or drawn by the user
dependencyRemoved@dependency-removedDependencyChangeEventDetailA dependency link was removed, including once per link a deleted task takes with it
taskDragged@task-draggedTaskDraggedEventDetailA bar was dropped at a new date
taskResized@task-resizedTaskResizedEventDetailA bar was resized by one of its handles
taskProgressChanged@task-progress-changedTaskProgressChangedEventDetailThe in-bar progress handle was dragged to a new value
selectionChange@selection-changeSelectionChangeEventDetailThe selected rows changed (needs enableSelection)
dependencyArrowUpdate@dependency-arrow-updateDependencyArrowUpdateDetailInternal redraw signal when a linked task moves; use dependencyAdded / dependencyRemoved for link changes
historyChange@history-changeHistoryChangeEventDetailThe undo/redo stack changed
sortChange@sort-changeSortChangeEventDetailThe active sort changed
filterChange@filter-changeFilterChangeEventDetailThe active filter changed
groupChange@group-changeGroupChangeEventDetailThe active grouping changed
columnResize@column-resizeColumnResizeEventDetailA task-list column was resized
columnReorder@column-reorderColumnReorderEventDetailTask-list columns were reordered

With apexgantt 3.20.0, taskUpdate and taskUpdateSuccess fire once per committed edit on every path (drag, resize, progress handle, dialog, inline edit, keyboard, updateTask()), with a source field naming the path, and taskDragged, taskResized and taskProgressChanged fire only after beforeTaskUpdate accepts the edit. An app that saves on both taskDragged and taskUpdateSuccess therefore saves each drag twice: save on taskUpdateSuccess alone. When a parent bar is dragged or moved with the keyboard, each moved task gets its own events, but all of the gesture's taskUpdate events arrive before its taskUpdateSuccess events.

Event Handling Example

<template>
  <ApexGanttChart
    :tasks="tasks"
    @task-dragged="handleTaskDragged"
    @task-resized="handleTaskResized"
    @task-update-success="handleTaskUpdateSuccess"
  />
</template>

<script setup lang="ts">
import type { 
  TaskDraggedEventDetail,
  TaskResizedEventDetail,
  TaskUpdateSuccessEventDetail 
} from 'vue-apexgantt';

const handleTaskDragged = (detail: TaskDraggedEventDetail) => {
  console.log('Task dragged:', detail);
  // save to backend, etc.
};

const handleTaskResized = (detail: TaskResizedEventDetail) => {
  console.log('Task resized:', detail);
};

const handleTaskUpdateSuccess = (detail: TaskUpdateSuccessEventDetail) => {
  console.log('Task updated:', detail);
};
</script>

Template Ref Methods

Access chart methods using template ref:

<template>
  <ApexGanttChart
    ref="ganttRef"
    :tasks="tasks"
  />
  
  <button @click="handleZoomIn">Zoom In</button>
  <button @click="handleZoomOut">Zoom Out</button>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { ApexGanttChart } from 'vue-apexgantt';

const ganttRef = ref<InstanceType<typeof ApexGanttChart> | null>(null);

const handleZoomIn = () => {
  ganttRef.value?.zoomIn();
};

const handleZoomOut = () => {
  ganttRef.value?.zoomOut();
};

// available methods:
// ganttRef.value?.update(options)
// ganttRef.value?.updateTask(taskId, data, options?)
// ganttRef.value?.zoomIn()
// ganttRef.value?.zoomOut()
// ganttRef.value?.destroy()
// ganttRef.value?.getInstance() // get raw ApexGantt instance
</script>
MethodDescription
update(options)Passes to the chart's update(). Leave out series and parsing to keep the chart's data, edits included (apexgantt 3.20.0)
updateTask(taskId, data, options?)Updates one task. skips taskUpdate and taskUpdateSuccess, for rollbacks and server echoes (apexgantt 3.20.0)
zoomIn() / zoomOut()Zoom by one step (the zoom is multiplied or divided by 1.4)
destroy()Removes the event listeners and destroys the chart. The component also does this when it unmounts
getInstance()The ApexGantt instance, or null before mount and after destroy()

Every other core method is on getInstance(), for example scrollToTask, addTask, deleteTask, undo, redo, sort, filter, getState and exportChart, plus getTask, getTasks, getDependencies, expandTask and collapseTask from apexgantt 3.20.0:

ganttRef.value?.getInstance()?.scrollToTask('task-2');

Prop changes and edits

The component sends only what changed to the chart's update():

ChangeSent to update()
tasks (a new array, or a change inside it), which replaces the chart's data: edits made in the chart are discarded and undo history is cleared
pixelsPerDay
theme plus your options (without series, parsing and pixelsPerDay), so custom colours in your options survive the switch. From apexgantt 3.20.0 a theme change resets only the colours the theme owns and keeps every other option; on earlier releases it reset other options to the theme's defaults
width, height,
optionsOnly the keys whose values changed. A key you remove falls back to the matching prop, or to the chart's default

With apexgantt 3.20.0, an update that leaves out series and parsing keeps the edits made in the chart (dragging, resizing, the dialog, inline editing, the keyboard, updateTask, addTask, deleteTask, moveTask and link changes), and keeps a zoom made from the toolbar unless pixelsPerDay is part of it. To save the chart, save getInstance()?.getTasks() together with getInstance()?.getDependencies() (both apexgantt 3.20.0). A task's dependency holds only the link it was loaded with, so links added or drawn later are only in getDependencies(), which also reflects undo and redo; the dependencyAdded and dependencyRemoved events do not fire on undo or redo, so a link list kept from them drifts. See Saving links for reloading them. To save each edit as it happens, listen to taskUpdateSuccess, taskAdded, taskDeleted and taskMoved. Writing them back into the array bound to tasks resends the whole array as series, so change tasks when you mean to replace the data. Earlier releases resent series and the default pixelsPerDay on every prop change, so a theme toggle discarded edits and reset the zoom.

Composables

useGanttData

Parse external data into ApexGantt format:

<script setup lang="ts">
import { ref } from 'vue';
import { ApexGanttChart, useGanttData } from 'vue-apexgantt';
import type { ParsingConfig } from 'vue-apexgantt';

// raw API data
const apiData = ref([
  {
    task_id: 'T1',
    task_name: 'Design Phase',
    start_date: '01-01-2024',
    end_date: '01-15-2024',
    completion: 0.75,
  },
  // ...more tasks
]);

// parsing configuration
const parsingConfig: ParsingConfig = {
  id: 'task_id',
  name: 'task_name',
  startTime: 'start_date',
  endTime: 'end_date',
  progress: {
    key: 'completion',
    transform: (value) => value * 100, // convert to percentage
  },
};

// parse data
const tasks = useGanttData({
  data: apiData,
  parsing: parsingConfig,
});
</script>

<template>
  <ApexGanttChart :tasks="tasks" />
</template>

Nested Objects & Transforms

Use dot notation for nested properties:

const nestedData = ref([
  {
    project: {
      task: { id: 'T1', title: 'Design' },
      dates: { start: '01-01-2024', end: '01-15-2024' },
      status: { completion: 0.75 },
    },
  },
]);

const tasks = useGanttData({
  data: nestedData,
  parsing: {
    id: 'project.task.id',
    name: 'project.task.title',
    startTime: 'project.dates.start',
    endTime: 'project.dates.end',
    progress: {
      key: 'project.status.completion',
      transform: (value) => value * 100,
    },
  },
});

Advanced Usage

Custom Options

<template>
  <ApexGanttChart
    :tasks="tasks"
    :options="ganttOptions"
    :pixels-per-day="25.7"
    theme="dark"
  />
</template>

<script setup lang="ts">
import type { GanttUserOptions } from 'vue-apexgantt';

const ganttOptions: Partial<GanttUserOptions> = {
  enableTaskDrag: true,
  enableTaskResize: true,
  enableTaskEdit: true,
  barBackgroundColor: '#537CFA',
  rowBackgroundColors: ['#FFFFFF', '#F8F9FA'],
  // ...more options
};
</script>

Dynamic Zoom

<template>
  <div>
    <select v-model.number="pixelsPerDay">
      <option :value="80">Day</option>
      <option :value="25.7">Week</option>
      <option :value="4.9">Month</option>
      <option :value="1.6">Quarter</option>
      <option :value="0.5">Year</option>
    </select>
    
    <ApexGanttChart
      :tasks="tasks"
      :pixels-per-day="pixelsPerDay"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const pixelsPerDay = ref(4.9);
</script>

Theme Switching

<template>
  <div>
    <button @click="toggleTheme">
      {{ theme === 'light' ? 'Dark' : 'Light' }}
    </button>
    
    <ApexGanttChart
      :tasks="tasks"
      :theme="theme"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import type { ThemeMode } from 'vue-apexgantt';

const theme = ref<ThemeMode>('light');

const toggleTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
};
</script>

Column Configuration

Customize the task list columns with columnConfig inside the options prop:

<template>
  <ApexGanttChart :tasks="tasks" :options="ganttOptions" height="500px" />
</template>

<script setup lang="ts">
import type { GanttUserOptions } from 'vue-apexgantt';

const ganttOptions: Partial<GanttUserOptions> = {
  columnConfig: [
    { key: 'name',      title: 'Task Name', 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 },
  ],
};
</script>

Annotations

Highlight specific dates or date ranges with visual annotations:

<template>
  <ApexGanttChart :tasks="tasks" :options="ganttOptions" height="500px" />
</template>

<script setup lang="ts">
import type { GanttUserOptions } from 'vue-apexgantt';

const ganttOptions: Partial<GanttUserOptions> = {
  annotations: [
    {
      x1: '03-15-2024', // parsed with inputDateFormat, 'MM-DD-YYYY' by default
      x2: '03-31-2024',
      label: {
        text: 'Sprint 1',
        fontColor: '#333333',
      },
    },
    {
      x1: '04-01-2024',
      label: {
        text: 'Go Live',
        fontColor: '#008FFB',
      },
    },
  ],
};
</script>

TypeScript Support

The package includes full TypeScript definitions. Import types as needed:

import type {
  TaskInput,
  ThemeMode,
  GanttUserOptions,
  ParsingConfig,
  TaskUpdateEventDetail,
  TaskDraggedEventDetail,
  TaskResizedEventDetail,
  HistoryChangeEventDetail,
  // ...and the detail type of every other event
} from 'vue-apexgantt';

TaskType, ColumnKey, GanttEvents, LightTheme, DarkTheme and DataParser are exported as values for runtime use (import { TaskType } from 'vue-apexgantt').