Skip to main content

Getting Started with SVAR Gantt in Nuxt

This guide takes you from an empty Nuxt project to a working Gantt chart. The finished project lives on the basic branch at github.com/svar-widgets/vue-gantt-demo-nuxt.

We'll add one capability at a time. After every stage there's something new on screen. That includes a couple of errors that are more useful to see and fix than to skip past.

1. Start a new project

Run the following commands to scaffold the project and install the package:

npm create nuxt@latest my-gantt-app
cd my-gantt-app
npm install @svar-ui/vue-gantt

2. Add the Gantt component

The chart relies on browser-only APIs, so it belongs on the client side. Create app/components/GanttChart.vue:

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

const tasks = [
{ id: 1, text: "Project planning", type: "summary", progress: 40, parent: 0, open: true },
{ id: 10, text: "Marketing analysis", start: new Date(2026, 3, 2), duration: 3, progress: 100, parent: 1 },
{ id: 11, text: "Discussions", start: new Date(2026, 3, 5), duration: 2, progress: 72, parent: 1 },
{ id: 12, text: "Approval of strategy", start: new Date(2026, 3, 8), type: "milestone", parent: 1 },
// ... more tasks
];

const links = [
{ id: 1, source: 10, target: 11, type: "e2s" },
{ id: 2, source: 11, target: 12, type: "e2s" },
// ... more links
];

const scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "day", step: 1, format: "%j" },
];
</script>

<template>
<Gantt :tasks="tasks" :links="links" :scales="scales" />
</template>

You can control the whole chart through three inputs:

  • tasks is a flat list, and parent turns it into a tree (0 is the root). Mark a grouping row with type: "summary". It stretches to cover its children dates on its own. type: "milestone" renders a single dated marker. Anything else is an ordinary bar defined by a start and a duration in days.
  • links connect tasks. "e2s" is end-to-start: the target can't begin until the source finishes.
  • scales describe the header rows over the timeline: a month band above a day band here.

Reference it from app/pages/index.vue:

<template>
<div>
<header class="page-header">
<h1>My Gantt</h1>
</header>
<main>
<GanttChart />
</main>
</div>
</template>

Nuxt auto-imports anything under app/components/, so you don't need to import it explicitly.

Run npm run dev. The page loads empty. Nothing is actually broken. The chart expands to fill its container, and that container currently has zero height.

3. Configure the chart height

Gantt takes the full size of whatever box wraps it, so it needs a real height to appear. A full-height page layout is the easiest way there. Adjust app/pages/index.vue:

<template>
<div class="demo-container">
<header class="demo-header">
<h1>My Gantt</h1>
</header>
<main class="demo-main">
<GanttChart />
</main>
</div>
</template>

<style scoped>
.demo-container {
display: flex;
flex-direction: column;
height: 100vh;
}
.demo-main {
flex: 1;
min-height: 0;
}
</style>

The header keeps its natural size and flex: 1 hands the remaining viewport to the chart. Reload the page. The tasks and timeline appear, but only as bare, unstyled markup. The stylesheet is still missing.

4. Import the styles

Import the CSS in GanttChart.vue:

<script setup>
import { Gantt } from "@svar-ui/vue-gantt";
import "@svar-ui/vue-gantt/all.css";

The Gantt package ships two CSS files:

  • all.css: chart plus styles for companion pieces like the toolbar, editor, and context menu
  • style.css: chart only. Use this when you import companion styles separately to keep the bundle small

After you reload the page, it looks like a Gantt now: a task grid on the left, bars along a timeline on the right. But there are no colors or interaction cues. That's because you haven't wrapped a theme provider around it yet.

5. Wrap it in a theme

SVAR uses a theme provider for visual styling. Wrap the chart in Willow:

<script setup>
import { Gantt, Willow } from "@svar-ui/vue-gantt";
import "@svar-ui/vue-gantt/all.css";

// ... tasks, links, scales definitions ...
</script>

<template>
<Willow>
<Gantt :tasks="tasks" :links="links" :scales="scales" />
</Willow>
</template>

Give the .wx-theme wrapper the same explicit height. Add this to a global stylesheet (app/assets/css/main.css, registered with css: ['~/assets/css/main.css'] in nuxt.config.ts):

html, body { height: 100%; margin: 0; }
.wx-theme { height: 100%; }

Reload the page. You now have colored summary bars, progress fills, dependency arrows, and hover feedback.

For dark mode, replace Willow with WillowDark.

6. Fix the hydration warning

Check the browser console and you'll probably find:

Hydration completed but contains mismatches.

This warning is expected. The chart calculates layout from its container dimensions. During SSR there's no DOM to measure, so the server renders with defaults. The client then hydrates with real dimensions, Vue detects the mismatch, and logs the warning.

You have two options.

Option A - leave it be. The component works correctly regardless. You'll notice a quick layout settle on first paint.

Option B - render client-only. Wrap the component in Nuxt's built-in <ClientOnly> on the page:

<template>
<div class="demo-container">
<header class="demo-header">
<h1>My Gantt</h1>
</header>
<main class="demo-main">
<ClientOnly>
<GanttChart />
</ClientOnly>
</main>
</div>
</template>

<ClientOnly> is built into Nuxt. It skips SSR for its children, so there's nothing to mismatch. The chart appears after mount.

7. Fix build errors

If the build fails on the SVAR packages, add them to the transpile list in nuxt.config.ts:

export default defineNuxtConfig({
build: {
transpile: [
'@svar-ui/vue-gantt',
],
},
})

That routes these ESM packages through Vite during the build. Since the Gantt depends on a handful of other @svar-ui/* packages internally, a scope-wide pattern saves you from listing each one:

build: {
transpile: [/@svar-ui\//],
}

8. Shape the timeline

The scales array is what you tune to change the header. Every entry becomes a row. Add a format to the day row plus a css callback that flags weekends:

function dayStyle(date) {
const weekend = date.getDay() === 5 || date.getDay() === 6;
return weekend ? "sday" : "";
}

const scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "day", step: 1, format: "%j", css: dayStyle },
];

%F prints the full month name and %j the day number. Beyond day and month, unit accepts "week", "quarter", "year", and "hour", while step sets how many units fit in a cell. For example, { unit: "week", step: 1 } yields one cell per week. Whatever class your css callback returns can then be targeted from your stylesheet. For the full set of options, see the scales configuration guide.

9. Add the editor

Users edit tasks through a sidebar form: the Editor component, which ships in the same package. Import it. Then capture the chart's API object via the init prop so the two stay in sync:

<script setup lang="ts">
import { ref } from "vue";
import type { IApi } from "@svar-ui/vue-gantt";
import { Gantt, Willow, Editor } from "@svar-ui/vue-gantt";
import "@svar-ui/vue-gantt/all.css";

// ... dayStyle, tasks, links, scales ...

const api = ref<IApi | null>(null);

function init(ganttApi: IApi) {
api.value = ganttApi;
}
</script>

<template>
<Willow>
<Gantt :tasks="tasks" :links="links" :scales="scales" :init="init" />
<Editor :api="api" />
</Willow>
</template>

The two moving parts:

  • :init="init" runs once on mount and passes you the chart's API object, which we hold in a ref.
  • <Editor :api="api" /> listens on that API and reveals a task's fields whenever a user opens one.

Double-click a bar (or its row in the grid) and the sidebar slides in with the task name, dates, progress, and type. Whatever you change writes straight back to the chart.

At this point you have a working Gantt with a task tree, dependency links, an adjustable timeline, and an editor. To persist changes to a server, continue with Adding a Backend.