@ganttloom/gantt-vue

Vue 3 wrapper for @ganttloom/gantt-core — fully customizable Gantt chart component.
1,513
Install
npm install @ganttloom/gantt-vue
Latest Version:0.6.3
Vue:3
License:MIT
Last Updated:Oct 21, 2026
Links: npm

@ganttloom/gantt-vue

npm license

Vue 3 component wrapping @ganttloom/gantt-core. A thin wrapper: it owns no scheduling logic of its own, just Vue lifecycle, props-as-events, and an exposed handle for the escape hatches (undo/redo, zoom, export) that don't fit the declarative-props model.

ganttloom overview — day-view Gantt chart with dependencies, critical path, and baselines

Install

npm install @ganttloom/gantt-vue
# vue >=3.3 is a peer dependency

Quick start

<script setup lang="ts">
import { GanttChart } from "@ganttloom/gantt-vue";

const tasks = [
  { id: "1", name: "Design", start: new Date("2026-01-05"), end: new Date("2026-01-09"), progress: 60 },
  { id: "2", name: "Build", start: new Date("2026-01-09"), end: new Date("2026-01-16"), progress: 10 },
];
const dependencies = [{ fromId: "1", toId: "2", type: "FS" as const }];
</script>

<template>
  <GanttChart
    :tasks="tasks"
    :dependencies="dependencies"
    view-mode="day"
    show-critical-path
    enable-history
    @date-change="(task, start, end) => console.log(task.name, start, end)"
  />
</template>

<GanttChart /> injects @ganttloom/gantt-core's default stylesheet automatically (a <style> tag added to <head> on first mount; no CSS import, so the package also loads under SSR/Node/Jest). Props mirror @ganttloom/gantt-core's GanttOptions one-to-one (in kebab-case, per Vue convention) — see that package's README for what each one does. Pass new array/object references to trigger a sync (setTasks/setDependencies/setOptions underneath); mutating in place won't re-render.

class/style fall through onto the chart's root element automatically — there's no separate prop for them. Option props like :show-baseline="true" enable planned baseline comparison and automated tooltip slippage variance tracking (with SVG diamond rendering for milestones), while :show-critical-path="true" highlights the critical path.

Imperative handle

Props/events cover the declarative surface (tasks, dependencies, view options, event listeners). For everything else, use a template ref:

<script setup lang="ts">
import { ref } from "vue";
import { GanttChart, type GanttChartHandle } from "@ganttloom/gantt-vue";

const chartRef = ref<GanttChartHandle | null>(null);
</script>

<template>
  <button @click="chartRef?.undo()">Undo</button>
  <button @click="chartRef?.zoomIn()">Zoom in</button>
  <GanttChart ref="chartRef" :tasks="tasks" enable-history />
</template>

The exposed GanttChartHandle has: undo/redo/canUndo/canRedo, expandAll/collapseAll/toggleGroup, removeDependency, updateDependency, updateTask, setViewMode, zoomIn/zoomOut/getZoomLevel, fitToViewport, scrollToToday, scrollToRangeStart/scrollToRangeEnd, getPageCount/setPage, getResourceHistogram, getLeveledTasks, selectTask/selectAllVisible/clearSelection/getSelectedTaskIds/bulkShiftDates/bulkDelete, setSort/getSort, toSVGString/toPNGDataURL/toPNGDataURLs, getRenderModel, and getInstance() for direct access to the underlying @ganttloom/gantt-core GanttChart when you need something not on this list.

Multi-select and bulk actions

<GanttChart
  :tasks="tasks"
  selectable
  @selection-change="(taskIds) => (selectedCount = taskIds.length)"
/>

Click selects one bar, ctrl/cmd-click toggles it into the selection, shift-click selects a range. Then drive it from the handle: chartRef.value?.bulkShiftDates(dayMs), chartRef.value?.bulkDelete().

Row reorder and dependency editing

<GanttChart
  :tasks="tasks"
  @task-reorder="(draggedId, targetId, position) => { /* position: before | after | inside */ }"
  @dependency-dblclick="(dep) => {
    // show your own 'edit link' UI, then:
    chartRef?.updateDependency(dep.fromId, dep.toId, { type: 'SS' });
  }"
/>

Both off by default (same construction-time-only wiring caveat as task-create below — attach the listener from the first render). Double-clicking a task bar itself fires the separate @task-dblclick="(task) => ...", same caveat.

Filling the viewport at any zoom level

A short project at month/year zoom, or just a handful of tasks, naturally renders narrow. auto-fit-to-viewport stretches (never shrinks) the timeline to fill the container instead of leaving empty space:

<GanttChart :tasks="tasks" auto-fit-to-viewport />

Off by default — upgrading doesn't silently change an existing chart's pixel layout for anyone who hasn't opted in.

Always on, separately: the visible range is padded to a minimum tick count per zoom level, so a short project at year/quarter zoom never collapses into one or two lonely columns, and the date header stays pinned to the top while scrolling a tall task list vertically (still scrolling horizontally in sync with the bars).

Header position

header-position places the grid and timeline headers at "top" (the default), "bottom", or "none" to hide them entirely:

<GanttChart :tasks="tasks" header-position="bottom" />

Drag-to-create

Off by default; attach a @task-create listener to turn it on (the core only wires the drag listener when a creation handler is present, so existing consumers see no behavior change):

<GanttChart :tasks="tasks" @task-create="(task) => console.log('created', task)" />

The core only checks for a listener at mount (same caveat as the other gated events above) — attach it from the first render if you want drag-to-create enabled; toggling it on after mount won't retroactively wire it.

Timeline markers, column resize/reorder, and search

<script setup lang="ts">
import { filterTasks } from "@ganttloom/gantt-vue";
</script>

<template>
  <GanttChart
    :tasks="filterTasks(tasks, (t) => t.name.includes(query)).tasks"
    :markers="[{ date: releaseDate, label: 'Release' }]"
    @column-resize="(id, width) => (columnWidths[id] = width)"
    @column-reorder="(order) => (columnOrder = order)"
  />
</template>

See @ganttloom/gantt-core's README for the full behavior of each — the props/events here are 1:1 mirrors of its GanttOptions fields, and filterTasks is a pure re-export.

Column sorting

Mark a column sortable; clicking its header cycles asc → desc → none:

<GanttChart
  :tasks="tasks"
  :columns="[{ id: 'name', title: 'Task', sortable: true }]"
  @sort-change="(sort) => console.log(sort)"
/>

chartRef.value?.setSort(columnId, direction) / .getSort() are also available on the handle.

Resource leveling and multi-page PNG export

const { tasks: leveled, shifted } = chartRef.value!.getLeveledTasks();
if (shifted.length) tasks.value = leveled; // if you're holding tasks in your own state

const pages = await chartRef.value!.toPNGDataURLs({ pageWidthPx: 1600, pageHeightPx: 1200 });

Resource histogram

<script setup lang="ts">
import { computed } from "vue";
import { renderResourceHistogramSVG } from "@ganttloom/gantt-vue";

const histogramSvg = computed(() => renderResourceHistogramSVG(chartRef.value?.getResourceHistogram() ?? []));
</script>

<template>
  <div v-html="histogramSvg" />
</template>

Hover tooltips

On by default (name/dates/progress/assignees/notes), styled via plain CSS (.gantt-tooltip, see gantt-core's README) — not the browser's native tooltip. renderTooltip fully replaces the content:

<GanttChart
  :tasks="tasks"
  :render-tooltip="(task) => (task.isMilestone ? null : `${task.name} — ${task.progress ?? 0}%`)"
/>

Set :show-tooltip="false" to disable it entirely.

i18n / overridable labels

A handful of built-in strings (aria-labels, the default name for a newly-created task, the tooltip's date/progress formatting) can be overridden via the labels prop — see gantt-core's README for the full field list:

<GanttChart
  :tasks="tasks"
  :labels="{
    expand: 'Développer',
    collapse: 'Réduire',
    newTaskName: 'Nouvelle tâche',
    formatDate: (d) => d.toLocaleDateString('fr-FR'),
  }"
/>

labels.formatTickLabel overrides the timeline header's own day/week/month tick labels (distinct from formatDate above, which only covers the tooltip and bar aria-label) - it receives each tick's Date and the current view-mode:

<GanttChart
  :tasks="tasks"
  view-mode="week"
  :labels="{ formatTickLabel: (date, viewMode) => date.toLocaleDateString('fr-FR') }"
/>

Custom sort comparator

Set sortFn on a sortable GanttColumn to fully control its comparison instead of the default accessor-based one - see gantt-core's README for details:

<GanttChart
  :tasks="tasks"
  :columns="[
    {
      id: 'priority',
      title: 'Priority',
      accessor: (t) => t.data?.priority,
      sortable: true,
      sortFn: (a, b) => (a.data?.priority === 'high' ? -1 : 1),
    },
  ]"
/>

Custom task-bar rendering

render-bar lets you draw custom SVG content into a task bar (icons, patterns, extra shapes) without forking the renderer - see gantt-core's README for the append-only contract that keeps drag/resize hit-testing intact:

<GanttChart
  :tasks="tasks"
  :render-bar="
    ({ task, bar, group }) => {
      if (!task.data?.flagged) return;
      const icon = document.createElementNS('http://www.w3.org/2000/svg', 'text');
      icon.setAttribute('x', String(bar.width + 6));
      icon.setAttribute('y', '-4');
      icon.textContent = '⚑';
      group.appendChild(icon);
    }
  "
/>

Arbitrary attributes on the root element

<GanttChart> has a single root <div>, so any attribute you pass that isn't a declared prop - id, data-*, aria-*, class, style, etc. - falls through onto it automatically (Vue's default attribute inheritance), no extra prop needed:

<GanttChart :tasks="tasks" data-testid="project-gantt" aria-label="Project schedule" />

useGanttChart (lower-level composable)

If you want to own the container element/DOM tree yourself instead of using <GanttChart />:

<script setup lang="ts">
import { ref } from "vue";
import { useGanttChart, type GanttTask, type GanttDependency } from "@ganttloom/gantt-vue";

const props = defineProps<{ tasks: GanttTask[]; dependencies: GanttDependency[] }>();
const containerRef = ref<HTMLDivElement | null>(null);
const { chart, canUndo, canRedo } = useGanttChart(
  containerRef,
  () => props.tasks,
  () => props.dependencies,
  () => ({ enableHistory: true })
);
</script>

<template>
  <div ref="containerRef" />
</template>

useGanttChart takes getter functions (not refs) for tasks/dependencies/options so it can track whatever reactive source each one reads — a prop, a ref, a computed.

Re-exported types & helpers

For convenience, this package re-exports every @ganttloom/gantt-core type (GanttTask, GanttDependency, GanttColumn, GanttTheme, ViewMode, WorkingCalendar, ConstraintType, ResourceLoadBucket, etc.) and the standalone helper functions (computeWBSCodes, applyConstraint, isWorkingDay, nextWorkingDay, computeResourceHistogram, renderResourceHistogramSVG, ...) so you never need @ganttloom/gantt-core as a direct dependency just to reference them.

Related Packages

@mogura/moguchart-vue

軽量でフレームワーク非依存のWeb Components製ガントチャートコンポーネント (Lit)...

1,202 4
@fluxgantt/vue

FluxGantt Vue 3 wrapper — component + useFluxGantt composable.

797 0
v-gantt-chart

display gantt-like vaule,like use this to order bus schedule.

7,650 706
m-gantt-chart

display gantt-like vaule,like use this to order bus schedule.

991 706

Version History

Version Released License
0.6.3 MIT
0.6.2 MIT
0.6.1 MIT
0.6.0 MIT
0.5.1 MIT