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-progressThe 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
| Field | Type | Notes |
|---|---|---|
id | string | Required. Unique across all rows. |
name | string | Display label (also the default sidebar column). |
tasks | Task[] | Required. May be empty (e.g. a group header row). |
level | number | optional Indent depth. Set by plugin-tree if omitted. |
parentId | string | optional Parent row id drives the tree hierarchy. |
hasChildren | boolean | optional Renders a collapse chevron in the sidebar. |
expanded | boolean | optional Initial expand state for a parent row. |
[key: string] | unknown | Extra fields are readable by column formatters. |
Task
| Field | Type | Notes |
|---|---|---|
id | string | Required. Unique across all tasks (dependencies reference it). |
name | string | Rendered as the bar label. |
start | string | Date | Required. Parsed by the date adapter. |
end | string | Date | Required. Inclusive a same-day start/end is one day wide. |
kind | 'task' | 'milestone' | optional A milestone renders as a diamond at end. |
progress | number | optional 0–1. Drawn by plugin-progress. |
dependencies | string[] | optional Predecessor task ids (finish-to-start). |
className | string | optional Added to the bar for custom styling. |
draggable | boolean | optional Per-task override of the engine draggable option. |
tooltip | string | optional 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
svgRenderersource for the pattern. - Cleanup. Always call
engine.destroy()(or unmount the Vue component) to remove listeners and dispose plugins in reverse order.