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.
Auto-scheduling, which moves dependent tasks as shown above, ships in 0.2.0 as the scheduling() plugin. Read the Dependencies and scheduling guide.
Three steps from install to chart.
The library code is the same in every framework. Only the element you mount into changes.
- 1Install
freeganttfrom npm. - 2Put your dated entries in a
Dataset. Ids and dates are plain strings. - 3Mount a
Gantton an element. Calldestroy()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();
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.
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 detailscontextMenu()Right-click actionsinlineEditing()Edit cells in the gridtimeShading()Weekends and closed timeimport { 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], });