Skip to main content
Open sourceMITTypeScript100 kB before gzip

An MIT-licensed Gantt chart for every stack.

FreeGantt is a TypeScript timeline library with no framework dependency. Mount it in a div from React, Vue, Svelte, Angular or plain HTML. Style it with CSS variables and extend it with plugins.

Open the live demo →

Auto-scheduling, which moves dependent tasks as shown above, ships in 0.2.0 as the scheduling() plugin. Read the Dependencies and scheduling guide.

Mounts into any element fromReactVueSvelteAngularSolidLitplain HTML
01 / QUICK START

Three steps from install to chart.

The library code is the same in every framework. Only the element you mount into changes.

  1. 1Install freegantt from npm.
  2. 2Put your dated entries in a Dataset. Ids and dates are plain strings.
  3. 3Mount a Gantt on an element. Call destroy() when the view goes away.
import { Gantt, Dataset } from 'freegantt';

const dataset = new Dataset({
  timeZone: 'America/Chicago',
  entries: [
    { id: 't1', name: 'Design', start: '2026-09-01', end: '2026-09-07' },
    { id: 't2', name: 'Build',  start: '2026-09-08', end: '2026-09-21' },
    { id: 't3', name: 'QA',     start: '2026-09-22', end: '2026-09-28' },
  ],
});

const gantt = new Gantt({ container: '#gantt', dataset });

// Edits are plain calls. The chart redraws on the next frame.
dataset.entries.update('t2', { end: '2026-09-24' });
dataset.undo();
02 / ACCESSIBILITY

Every edit works without a mouse.

Gantt charts are usually the least accessible part of a planning app. FreeGantt treats keyboard and screen reader users as first-class from the core up, and checks it on every change.

Keyboard
Move, resize, reorder, edit
Every task, every gesture, no pointer required.
Screen readers
ARIA grid and tree grid
Flat lists read as a grid. Nested plans read as a tree grid.
Feedback
Announced refusals
When an edit is blocked, a live region says so.
CI
axe on every demo page
Automated checks run in each built-in theme, not only the default.
03 / PLUGINS

The core draws the chart. Plugins add the rest.

Four plugins ship with the package. Leave one out and it stays out of your bundle. Write your own to paint bands behind or over the bars, add menus, or attach data behavior.

tooltips()Hover details
contextMenu()Right-click actions
inlineEditing()Edit cells in the grid
timeShading()Weekends and closed time
Plugin authoring guide →
highlight-late.ts
import { Gantt, tooltips, contextMenu, definePlugin } from 'freegantt';

// Paint a stripe behind every row that is running late.
const highlightLate = definePlugin({
  id: 'app.highlightLate',
  view(ctx) {
    ctx.view.registerDecoration('underBars', ({ rows }) =>
      rows
        .filter((row) => isLate(row.entryIds[0]))
        .map((row) => ({ kind: 'rowStripe', rowId: row.id, class: 'late' })),
    );
  },
});

new Gantt({
  container,
  dataset,
  plugins: [tooltips(), contextMenu(), highlightLate],
});
04 / PERFORMANCE

Built for plans with real size.

Open the performance demo →
50,000
entries in a single chart in the performance demo
100kB
bundle size before gzip
0
framework dependencies. Plugins you skip are never bundled.
05 / FEATURES

What you get in the box.

MIT licensed
Use it in commercial products. No seats, no license keys.
Undo and redo
Built into the Dataset. Transactions undo as one step.
IANA time zones
Set a zone per dataset so every viewer sees the same day boundaries.
Tree rows and roll-ups
Parents roll up from their children. Group or filter rows live.
Drag with snapping
Smooth preview while dragging, snapped on release. Hold Alt to place freely.
Server-friendly events
Save on change, or veto a move with an async before-handler.
Light and dark themes
Follows the OS by default. Override any CSS custom property.
Shared time axis
Two Gantts can share one scale and scroll position.

Your data. Your framework. Our chart.

Star on GitHub