@ganttloom/gantt-vue
| 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
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.

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
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 | |
| 0.5.0 | MIT | |
| 0.4.4 | MIT | |
| 0.4.3 | MIT | |
| 0.4.0 | MIT | |
| 0.2.0 | MIT |