Lattice Grid Buy a licence

demo D271

A project plan (Gantt) in ESM

A task list as a schedule: editable bars, the four dependency links, the critical path, milestones and progress, recomputing as you drag

createGantt

This is a project plan as an editable schedule: drag the bars, set the four kinds of dependency, and the plan recomputes start dates, slack, the critical path, milestones and progress. It works out the schedule from the tasks and their links, so you see straight away which tasks cannot slip without moving the finish.

Building…
Loading a live grid…

This is the ES module version. The grid is imported straight from a CDN as a module, the same configuration as the vanilla tab without a global script tag, so it drops into any bundler or a bare module script.

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">

<div id="tasks" style="height: 360px"></div>
<div id="plan" style="height: 360px; overflow: auto"></div>

<script type="module">
  import { createGantt } from '@toclocoinc/lattice-grid/modules/gantt';

  // tasks: id, name, start, duration, percentComplete, milestone
  // dependencies: { from, to, type: 'FS' | 'SS' | 'FF' | 'SF', lag? }

  // The left pane is a real editable grid over the task rows.
  const grid = createGrid(document.getElementById('tasks'), {
    rowKey: 'id',
    columns: [
      { field: 'name', title: 'Task', layout: { flex: 1, min: 120 } },
      { field: 'start', title: 'Start', type: 'number', layout: { width: 74 }, edit: true },
      { field: 'duration', title: 'Days', type: 'number', layout: { width: 68 }, edit: true },
    ],
    rows: tasks,
  });

  // The controller: bind it to the grid and name which fields carry the dates,
  // so a drag on the timeline writes back and a grid edit reflects on the bars.
  const gantt = createGantt({
    tasks,
    dependencies,
    grid,
    columns: { start: 'start', duration: 'duration' },
    autoSchedule: true,
  });

  gantt.mount(document.getElementById('plan'), {
    editable: true,
    // The tasks above are written as day offsets, so say which calendar date
    // day 0 stands for. The axis, bar labels, tooltips, screen-reader text and
    // weekend shading move onto real dates; the schedule, the state you save
    // and both exports keep counting in your own day numbers.
    projectEpoch: '2026-08-24',
    today: '2026-09-04',      // read through the anchor
    label: 'name',
    dateAxis: true,
    nonWorking: 'weekends',   // shade the weekends the schedule steps over
    rowHeight: 34,
    width: 860,
  });

  // Keep the two panes aligned as either scrolls vertically.
  gantt.view.linkVerticalScroll(grid.element);
</script>

A task list that draws its own schedule

Give the plan a list of tasks and how they depend on one another, and it works out the schedule for you: when each task can start and finish, how much slack it has, and which tasks have none. That last run, the tasks that cannot slip without moving the end date, is the critical path, and it is drawn in red so the risk in the plan is the first thing you see. Change a duration or drag a task and the whole schedule recomputes, so the picture is always the real one rather than a drawing someone forgot to update.

The timeline is not a screenshot. Drag a bar to move a task, or drag its right edge to make it shorter or longer, and the new dates are written back to the row underneath; edit a start or a duration in the task list beside it and the bar keeps up. Tasks can depend on one another in each of the four standard ways, finish-to-start through to start-to-finish, each with an optional lead or lag, and the arrows between the bars show how the plan holds together. Milestones sit as diamonds, each bar fills to show how far along it is, the timeline runs on a calendar axis with a line marking today, and weekends are shaded so working days read apart from days off.

A plan written in day offsets, drawn on this month

The plan behind this timeline holds no dates at all. It is written the way plans are first written: discovery on day 0, design on day 4, build on day 9. That is the natural way to describe work before anyone has agreed when it starts, and it is the form a template, an estimate or a plan copied from the last project arrives in.

A schedule counts in whole days, so a plan of offsets has nothing to tell it which day is day 0, and drawn as it stands it lands in January 1970. One setting fixes that: name the calendar date plan day 0 stands for, and the axis, the bar labels, the tooltips, the text a screen reader reads out and the shaded weekends all move onto real dates. The timeline above is anchored to a Monday a fortnight back, which is why the first weekend shading falls on days five and six.

It changes what is drawn and nothing else. The offsets in your data stay offsets, and every figure the schedule gives back, the state you save, the file you export and the plan you hand to Microsoft Project, is still counted in the day numbers you wrote. So a template can be shown against any start date you like without a copy of the plan being rewritten to match, and the same tasks can be drawn for a January start and an April one without the data differing by a single field. When you would rather the plan itself be on calendar dates, set the project start instead and the schedule moves with it.

The plan is honest about trouble. A task dragged earlier than its dependencies allow is flagged rather than quietly moved, so you see the conflict instead of shipping it. Anything that has slipped behind is marked, and because the schedule is built from the tasks a grid already holds, the same live feed that fills your grids can fill the plan too, keeping it current as the work moves.

How do I turn a task list into a Gantt chart?

Load the gantt module and call createGantt({ tasks, dependencies }), then mount(element, options) to draw it. Each task is a row with a duration (and, for a fixed placement, a start); a milestone is a task marked as one, and a task named as another’s parent becomes a summary rolled up from its children. Dependencies are { from, to, type } where the type is one of the four link kinds, with an optional lag. Pass a grid and a column map to bind the timeline to an editable task grid, so a drag writes the dates back and an edit in the grid redraws the bar. Turn on editable for drag-editing, set today for the today line, turn on dateAxis to read the timeline as calendar dates, and set nonWorking to 'weekends' to shade Saturdays and Sundays. If your tasks are written as day offsets rather than dates, pass projectEpoch with the calendar date day 0 stands for: it moves the axis, labels, tooltips, screen-reader text and weekend shading onto real dates and leaves the schedule, the saved state and both exports counted in your own day numbers. It applies to mount; to put the model itself on calendar dates, use projectStart instead.