Introduction / Getting started

Getting started

Install a renderer plus the plugins you want, compose them, and mount. There are no config files and no global registration a chart is a GanttEngine with renderer and feature plugins installed on it.

Install

Pick a renderer and add it alongside the core. Everything is published under the @ganttkit scope.

# vanilla SVG renderer + core
pnpm add @ganttkit/core @ganttkit/svg

# or the HTML renderer
pnpm add @ganttkit/core @ganttkit/html

# feature plugins are separate packages  add only what you use
pnpm add @ganttkit/plugin-columns @ganttkit/plugin-tree @ganttkit/plugin-progress

The core has zero runtime dependencies and every package ships ESM + CJS with types. Plugins are tree-shakeable, so an unused plugin costs nothing in your bundle.

Your first chart

createGantt from @ganttkit/svg is the fastest path: it builds a GanttEngine and installs the SVG renderer for you.

import { createGantt } from '@ganttkit/svg'
import '@ganttkit/svg/styles.css'

const gantt = createGantt({
  target: '#chart',      // a selector or an HTMLElement
  theme: 'dark',         // 'dark' | 'light'
  viewMode: 'Week',      // 'Day' | 'Week' | 'Month'
  rows: [
    {
      id: 'design',
      name: 'Design',
      tasks: [
        { id: 't1', name: 'Wireframes', start: '2026-07-01', end: '2026-07-08', progress: 1 },
        { id: 't2', name: 'Mockups',    start: '2026-07-09', end: '2026-07-20', progress: 0.4 },
      ],
    },
    {
      id: 'build',
      name: 'Development',
      tasks: [
        { id: 't3', name: 'API',      start: '2026-07-15', end: '2026-08-05', progress: 0.2 },
        { id: 'ms', name: 'Launch',   start: '2026-08-31', end: '2026-08-31', kind: 'milestone' },
      ],
    },
  ],
})

// later…
gantt.setViewMode('Month')
gantt.destroy()

The target element only needs a size the renderer fills it and scrolls internally:

<div id="chart" style="height: 480px"></div>

The explicit form

createGantt is sugar. When you want to add feature plugins, build the engine yourself and use() each plugin. The engine is chainable and recomputes after every install.

import { GanttEngine } from '@ganttkit/core'
import { svgRenderer } from '@ganttkit/svg'
import { createColumns } from '@ganttkit/plugin-columns'
import { progressPlugin } from '@ganttkit/plugin-progress'
import '@ganttkit/svg/styles.css'

const engine = new GanttEngine({ rows, viewMode: 'Week' })

engine.use(svgRenderer({ target: '#chart', theme: 'dark' }))
engine.use(createColumns({ columns: [{ key: 'name', label: 'Task' }] }).plugin)
engine.use(progressPlugin())

Some plugins return a controller object whose .plugin you install (createColumns(…).plugin) the controller also carries imperative methods. Others return the plugin directly (progressPlugin()). The Using plugins page notes which is which.

The data model

A chart is an array of rows; each row owns an array of tasks. Dates are anything the date adapter can parse an ISO string ('2026-07-01') or a Date.

Row

FieldTypeNotes
idstringRequired. Unique across all rows.
namestringDisplay label (also the default sidebar column).
tasksTask[]Required. May be empty (e.g. a group header row).
levelnumberoptional Indent depth. Set by plugin-tree if omitted.
parentIdstringoptional Parent row id drives the tree hierarchy.
hasChildrenbooleanoptional Renders a collapse chevron in the sidebar.
expandedbooleanoptional Initial expand state for a parent row.
[key: string]unknownExtra fields are readable by column formatters.

Task

FieldTypeNotes
idstringRequired. Unique across all tasks (dependencies reference it).
namestringRendered as the bar label.
startstring | DateRequired. Parsed by the date adapter.
endstring | DateRequired. Inclusive a same-day start/end is one day wide.
kind'task' | 'milestone'optional A milestone renders as a diamond at end.
progressnumberoptional 0–1. Drawn by plugin-progress.
dependenciesstring[]optional Predecessor task ids (finish-to-start).
classNamestringoptional Added to the bar for custom styling.
draggablebooleanoptional Per-task override of the engine draggable option.
tooltipstringoptional Native SVG <title> text.
const rows = [
  {
    id: 'platform', name: 'Platform', level: 0, hasChildren: true, expanded: true, tasks: [],
  },
  {
    id: 'api', name: 'API', level: 1, parentId: 'platform',
    tasks: [
      { id: 'a1', name: 'Schema',  start: '2026-07-01', end: '2026-07-10', progress: 1,  className: 'task-completed' },
      { id: 'a2', name: 'Gateway', start: '2026-07-11', end: '2026-07-28', progress: 0.3, dependencies: ['a1'] },
    ],
  },
]

Theming

The renderer adds the .gantt class and a data-theme attribute to your target element and ships a stylesheet of CSS custom properties. Override any --gk-* variable to re-brand this is exactly how the demo applies the orange palette.

/* load the renderer theme first, then override */
@import '@ganttkit/svg/styles.css';

.gantt[data-theme='dark'] {
  --gk-accent:       #f0a030;
  --gk-bar:          rgba(240,160,48,.42);
  --gk-bar-stroke:   #f0a030;
  --gk-today-bg:     rgba(240,160,48,.13);
  --gk-progress:     rgba(61,214,140,.55);
  --gk-milestone:    #f0a030;
}

/* task classNames become bar modifiers */
.gantt .gantt-bar.task-completed { fill: rgba(61,214,140,.5); stroke: #3dd68c; }

Switch themes at runtime by toggling the attribute on the same element:

const el = document.querySelector('#chart')
el.setAttribute('data-theme', el.getAttribute('data-theme') === 'dark' ? 'light' : 'dark')
!

Load the renderer's styles.css before your overrides. Both target .gantt[data-theme=…] at equal specificity, so source order decides the winner.

Framework notes

  • SSR. The core never touches the DOM, so it computes fine on the server. Renderers mount on the client.
  • Vue / React / Svelte / Angular. There's no official adapter yet, but a renderer is a thin plugin see Writing a plugin and the svgRenderer source for the pattern.
  • Cleanup. Always call engine.destroy() (or unmount the Vue component) to remove listeners and dispose plugins in reverse order.