Timeline

A week-based project timeline: bars packed into lanes over a row of days, with a today marker, week navigation, search and filters.

Timeline0
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/timeline.json
Usage
Pass the columns and rows a data grid takes, name the start and end columns, then render the timeline.
TSImport
import { Timeline } from "@/components/blocks/timeline";
TSExample
"use client";

import { Timeline } from "@/components/blocks/timeline";

const columns = [
  { id: "name", header: "Project" },
  { id: "start", header: "Start", type: "date" },
  { id: "due", header: "Due", type: "date" },
];

const rows = [
  { id: "1", name: "Easter motion pack", start: "2026-09-28", due: "2026-10-01" },
  { id: "2", name: "Christmas series brand", start: "2026-09-30", due: "2026-09-30" },
];

export function Example() {
  return (
    <Timeline
      columns={columns}
      rows={rows}
      startField="start"
      endField="due"
      defaultDate={new Date(2026, 8, 28)}
    />
  );
}
Composition
Anatomy of the timeline.
Timeline (.card-surface shell, data-slot="timeline")
├── Toolbar band (.records-toolbar — hidden by hideHeader)
│   ├── icon + title + count pill   (a phone keeps only the icon)
│   ├── search      → TextField, matches the title and every text column
│   ├── Filters     → the filter-builder block, one field per column
│   └── Customize   → Popover of Switches: Show avatars, Show badge
├── Navigation band (todayButton / dateNavigation)
│   ├── Today       → Button
│   └── prev / DatePicker mode="week" / next
└── Scroller (scroll-fade-x; vertical fade omitted for the sticky header)
    ├── Day header  (sticky, one cell per day, today in the brand colour)
    └── Body
        ├── column rules + today marker (brand line)
        ├── Lanes   → bars packed greedily into the first free lane,
        │             clamped to the visible days
        │   └── Bar card (.card-surface)
        │       ├── title (Tooltip) + ⋯ DropdownMenu
        │       ├── "Mon 14 – Wed 16"
        │       ├── status Badge + AvatarGroup
        │       └── bottom stripe in the colorField option's colour
        └── Empty when no bar falls in view

Data
├── columns / rows — the very arrays a DataGridV3 takes
├── startField / endField → date columns ("YYYY-MM-DD"); no end = one day, no start = skipped
├── usersField  → user column (roster resolves ids to names)
├── badgeField  → status column (label + colour on the Badge)
└── colorField  → status / chips column tinting the stripe (defaults to badgeField)
Timeline
Pass the same `columns` and `rows` you give a data grid, and name the columns that mean start, end, owners and status. Bars are packed greedily into the first lane with room and clamped to the visible days; a bar that started last week or runs on into next shows its real dates but is cut at the edge. The toolbar carries search over the title and every text column, the Filters popover (one field per column), and Customize to toggle avatars and the status badge. Today's column and marker use the brand colour, and the scroller scrolls sideways on a phone while the toolbar stays one row.
Timeline0
Two weeks
`days={14}` widens the range; previous and next step a fortnight and the week picker reads as the whole span. `columnWidth` sets the minimum width of a day, and the columns stretch to fill the card when there is room.
Timeline0
Your own header
`hideHeader` drops the toolbar and keeps the timeline. Search and filters take a value and hand back a change (`search` / `onSearchChange`, `filters` / `onFiltersChange`), and `onViewChange` reads back the counts, the `filterFields` for your own `FilterBuilder`, and the visible `range`.
0 projects
Minimal
Bare bars: `showAvatars`, `showBadge` and `showStripe` off, no heading, and `customizable={false}` drops the Customize popover.
0
API Reference
Props for the timeline. Columns and rows are the data grid's own shapes.
PropTypeDefaultDescription
columnsTimelineColumn[]—The columns array you give a data grid: `{ id, header, type, options, users }`.
rowsTimelineRow[]—The rows array you give a data grid: `{ id, ...values }`.
startFieldstring"start"Id of the `date` column holding each bar's first day. A row without one is skipped.
endFieldstring—Id of the `date` column holding the last day. A row without one is a one-day bar.
titleFieldstring"name"Column read as the card title.
usersFieldstring—A `user` column; its `users` roster resolves ids to names and avatars.
badgeFieldstring—A `status` column; its option `label` and `color` render as a Badge.
colorFieldstringbadgeFieldA `status` or `chips` column whose option `color` tints the card's bottom stripe. Uses the Badge dot colours.
titlestring | false"Timeline"Toolbar heading. `false` drops it.
iconReactNode | false—Glyph beside the title. `false` drops it.
daysnumber7Days in view. Previous / next step by this many.
weekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 61First day of the week; the range always starts on one.
dateDate—Any day in the visible range, controlled. Defaults to today.
defaultDateDate—Uncontrolled starting day. Pass one when the server must render bars (today is only known after mount).
onDateChange(date: Date) => void—Fires with the first day of the new range.
columnWidthnumber162Minimum width of a day column in px. Columns stretch to fill; below the total the scroller scrolls sideways.
cardHeightnumber108Height of a bar card in px.
rowGapnumber30Vertical gap between lanes in px.
cardInsetnumber20Horizontal padding of a card inside its cells in px.
showTodaybooleantrueDraw the today marker line.
todayButtonbooleantrueShow the Today button.
dateNavigationbooleantrueShow previous / next and the week picker.
showAvatarsboolean—Avatars on each card, controlled.
defaultShowAvatarsbooleantrueUncontrolled starting value.
onShowAvatarsChange(show: boolean) => void—Fires when the Customize switch flips.
showBadgeboolean—Status badge on each card, controlled.
defaultShowBadgebooleantrueUncontrolled starting value.
onShowBadgeChange(show: boolean) => void—Fires when the Customize switch flips.
showDatesbooleantrueThe "Mon 14 – Wed 16" line on a card.
showStripebooleantrueThe coloured stripe along a card's bottom.
customizablebooleantrueRender the Customize popover. It only appears when `usersField` or `badgeField` is set.
searchableboolean | string—Toolbar search field over the title and every text column. A string sets the placeholder.
searchstring—The search text, controlled.
defaultSearchstring—The search text on first render, uncontrolled.
onSearchChange(search: string) => void—Fires with the new text.
filterableboolean—Show the Filters popover — the filter-builder block over every column.
filtersFilterGroup—The filter tree, controlled. Feed it from a `FilterBuilder` of your own using `filterFields` from `onViewChange`.
defaultFiltersFilterGroup—The filter tree on first render, uncontrolled.
onFiltersChange(filters: FilterGroup) => void—Fires with the whole tree whenever it changes.
hideHeaderbooleanfalseDrop the toolbar band and keep the timeline; search and filters still work through props.
onViewChange(view: { total, matchCount, filtering, filterFields, range: { from, to } }) => void—Fires whenever the view changes: the counts, the filter vocabulary and the visible range. The read side of the header API.
onRowClick(row: TimelineRow) => void—Fires when a card is clicked (alongside the details popover).
detailsboolean | ((row: TimelineRow) => ReactNode)trueClicking a card opens a popover with its title, date range, and every column as a term/value row (badges for options, avatars for people). `false` turns it off; a function renders the popover body yourself.
detailFieldsstring[]—Column ids the details popover lists, in order. Defaults to every column but the title.
rowMenuDropdownMenuItemSpec[] | ((row) => DropdownMenuItemSpec[]) | false—Items for each card's ⋯ menu. Defaults to an Open item when `onRowClick` is set; `false` hides the button.
emptyTitlestring—Empty state title. Defaults to "Nothing scheduled" ("No matches" while filtering).
emptyDescriptionstring—Empty state description.
fillbooleanfalseStretch to the parent's height; the lanes scroll inside, under the sticky day header.
classNamestring—Classes for the shell.