The same rows as a grid, a scheduler and a board
The Gantt is one of four ways the grid can render a set of rows. The others
are the table itself, the scheduler and the
Kanban board, and all four are the same <SvGrid>
with a different prop. This page is about running more than one of them over
one array: what to switch, how the edits get back, and the one data-shape rule
that keeps the views in step.
One grid, one prop per view
Each view is a prop on <SvGrid>; the rest of the props stay as they are:
{#if view === 'grid'}
<SvGrid {data} {columns} getRowId={(r) => r.id} sortable enableInlineEditing />
{:else if view === 'gantt'}
<SvGrid {data} {columns} getRowId={(r) => r.id} gantt={ganttCfg} />
{:else if view === 'scheduler'}
<SvGrid {data} {columns} getRowId={(r) => r.id} scheduler={schedulerCfg} />
{:else}
<SvGrid {data} {columns} getRowId={(r) => r.id} board={boardCfg} />
{/if}
data, columns and getRowId are shared, which is what makes the views
agree: a row is the same object with the same id in all four. The columns do
double duty - the table shows them, the Gantt's task pane lists the ones named
in tableColumns, and the scheduler's and board's drawers edit them with each
column's editorType.
Switching the prop remounts the view, so anything a view keeps for itself (the
Gantt's collapse state, the scheduler's current date, a lane's scroll) starts
over. Hoist what should survive: collapsed / onCollapseChange on the
Gantt, initialDate on the scheduler.
Which view for which question
| Question | View | The prop |
|---|---|---|
| What is the list, and can I edit a cell? | Table | none |
| When does each task run, and what depends on what? | Gantt | gantt |
| Who is busy when, and who has room? | Scheduler, timeline views | scheduler with resourceField |
| What state is each item in, and what is next? | Kanban | board with groupBy |
The Gantt and the scheduler overlap on "when". The Gantt is for a plan: a tree of tasks, links between them, a finish date the whole thing turns on. The scheduler is for bookings: rooms, shifts, appointments, one resource per row, with a calendar's month / week / day views. A task with an owner can go on either; a project with phases and dependencies belongs on the Gantt.
Edits flow one way: through callbacks
No view mutates a row. Each reports what it wants and you write it:
| View | Gesture | Callback |
|---|---|---|
| Table | inline edit commits | onCellValueChange (the value is already on the row) |
| Gantt | drag, resize, progress grip | onTaskMove, onTaskResize, onProgressChange |
| Scheduler | drag to a time or a resource, resize | onEventMove, onEventResize |
| Kanban | drag to a lane | onCardMove |
Because the rows are shared, an edit in one view is the next view's data.
Write the status in onCardMove and the table's Status column has it; write
the dates in onTaskMove and the scheduler's bar has moved. The demo above
names the last write and the view that asked for it under the switcher.
Derived fields are yours to keep consistent. The demo sets progress to 100
when a card lands on Done and flips the status to Done when the Gantt's
progress grip reaches 100, so neither view can contradict the other.
One date shape for every view
The views read a date-only string differently, and it is the one thing that will put them out of step:
- The Gantt reads
'2026-09-16'as the local calendar day, and anendFieldholding a date-only string as inclusive of that day. - The scheduler reads it with
new Date(...), which is UTC midnight - west of Greenwich that is the 15th - and its end is always exclusive.
So the same { start: '2026-09-14', end: '2026-09-16' } is a three-day bar on
the Gantt and a two-day event (or a day early) on the scheduler.
Store timestamps with an exclusive end when rows feed both. A local ISO
string without an offset ('2026-09-14T00:00') parses as local time in every
browser, and both views take a value carrying a time literally:
type Row = {
id: string
name: string
start: string // '2026-09-14T00:00' - local midnight
end: string // '2026-09-17T00:00' - exclusive: the 14th, 15th and 16th
allDay: boolean // true, so the scheduler draws a whole-day bar
}
const iso = (d: Date) =>
`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`
function writeSpan(row: Row, start: Date, end: Date) {
// Every view hands back `end` exclusive, so it is stored as it comes.
row.start = iso(start)
row.end = iso(end)
}
The table then formats the dates for display: end minus a day is the last
day a task runs, which is what a Finish column should say.
Phases and milestones are Gantt-only rows
A phase row carries no dates of its own; a milestone has no length. Both are
rows the Gantt draws (as a summary bar and a diamond) and the other views
would render as an empty card or an hour-long event. Give such rows a kind
and hand the table, scheduler and board the tasks only:
const tasks = $derived(rows.filter((r) => r.kind === 'task'))
The Gantt gets rows; the other three get tasks. Both are views of the same
array, so the write-back rule above still holds.
Four synced views at once
The switcher above shows one view at a time. Two of the scheduler demos put four views on screen together in a dockable workspace, all reading one array:
See also
- Kanban board mode - the
boardprop. - Scheduler / calendar mode - the
schedulerprop and its timeline views. - Editing - every Gantt callback and what arrives in it.
Live examples
- One plan, four views: Grid, Gantt, Scheduler, Kanban - One <SvGrid>, one array, four renderers behind a switch: the plain table with inline editing, the Gantt with phases and links, a resource timeline with a lane per owner, and a Kanban of status lanes. Every view writes back through a callback to the same rows - change a status in the grid and the card moves lane, drag a bar in the Gantt and the timeline follows - and a line under the switcher names the last write.
- Meridian Capital - research & earnings desk - One dataset, four synced views in a dockable workspace (SvDockManager): the same research events as a Scheduler, a Grid, a Kanban pipeline (by workflow stage), and a stacked exposure Chart. Edit in any view - it writes back to one array.
- Northwind People - recruiting pipeline - One dataset, four synced views in a dockable workspace (SvDockManager): the same candidates as an interview Scheduler, a Grid, a Kanban hiring pipeline (Applied to Hired), and a stacked hiring-funnel Chart. Edit in any view - it writes back to one array.
Related articles
- Building a Project / Task Board with a Svelte Data Grid - How to build a task management grid with grouping by status or assignee, inline edits, subtask tree rows, and saved views - without reaching for a dedicated project management tool.
- Paste from Excel into a Svelte Data Grid - How to wire up clipboard paste so users can drop a copied Excel or Google Sheets block directly into SvGrid - TSV parsing, type coercion, validation, and row growth all covered.
- Building an Order Management Dashboard in Svelte - How to wire up an order management grid with master-detail line items, status workflows, server-side data, and bulk fulfillment actions using SvGrid.