| title | Plugin Timeline |
|---|
import { InteractiveDemo } from '@/app/components/InteractiveDemo'; import { PluginLoader } from '@/app/components/PluginLoader';
Timeline component with vertical, horizontal, and Gantt-style layouts for displaying events and project timelines.
npm install @object-ui/plugin-timeline<PluginLoader plugins={['timeline']}>
// Import once in your app entry point
import '@object-ui/plugin-timeline'
import type { TimelineSchema } from '@object-ui/types'
// Use in schemas - Vertical Timeline
const schema: TimelineSchema = {
type: 'timeline',
variant: 'vertical',
items: [
{
time: '2024-01-15',
title: 'Project Started',
description: 'Kickoff meeting',
variant: 'success'
}
]
}- Three Layout Variants: Vertical, horizontal, and Gantt-style layouts
- Customizable Markers: Color-coded markers with icon support
- Date Formatting: Multiple date format options
- Gantt Charts: Project timeline visualization with task bars
- Time Scales: Hour, day, week, month, quarter, or year scales for Gantt view
- Lightweight: Pure CSS and React components
{
type: 'timeline',
variant?: 'vertical' | 'horizontal' | 'gantt',
items?: (TimelineFeedItem | TimelineGanttItem)[],
dateFormat?: 'short' | 'long' | 'iso',
// Gantt-specific
scale?: 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year',
rowLabel?: string,
minDate?: string,
maxDate?: string,
className?: string
}
Exported as TimelineFeedItem. These seven keys are the whole item — title
is required.
{
time?: string, // ISO date string
title: string,
description?: string,
variant?: 'default' | 'success' | 'warning' | 'danger' | 'info',
icon?: string, // Emoji or text
content?: SchemaNode | SchemaNode[], // Custom content below the description
className?: string
}
Exported as TimelineGanttItem (a row) and TimelineGanttItemBar (one bar).
label is required; a row with no bars yet may omit items.
{
label: string, // Row label
items?: Array<{
title?: string,
startDate?: string | number | Date, // ISO date string, or epoch milliseconds
endDate?: string | number | Date,
variant?: 'default' | 'success' | 'warning' | 'danger' | 'info'
}>
}
A bar whose dates cannot be drawn is not a validation failure: the chart shows its "unusable date range" message for it instead.
A date-only endDate is inclusive: the bar runs through the end of that day.
So 2024-01-01 to 2024-01-31 fills January, and a bar that starts and ends
on the same day is one day wide. An endDate with a time part, or given as a
number or a Date, is an instant, and the bar ends exactly there.
| Property | Type | Default | Description |
|---|---|---|---|
variant |
string | 'vertical' |
Timeline layout: vertical, horizontal, or gantt |
items |
array | [] |
Array of timeline items |
dateFormat |
string | 'short' |
Date formatting: short, long, or iso |
scale |
string | 'month' |
Gantt axis bucket: hour, day, week, month, quarter, or year |
rowLabel |
string | 'Items' |
Label for Gantt rows |
minDate |
string | auto | Override min date for Gantt (YYYY-MM-DD) |
maxDate |
string | auto | Override max date for Gantt (YYYY-MM-DD) |
className |
string | '' |
Additional Tailwind CSS classes |
Every key above is declared on the exported TimelineSchema, so an editor
completes them and a wrong value is a type error. scale is the canonical axis
key and the only one — it is @objectstack/spec's ui/TimelineConfig.json
spelling and the one the renderer reads.
object-timeline fetches records from objectName and draws one entry per
record, from the fields its timeline config names. An authored node takes its
props in the properties bag, where @objectstack/spec's
ComponentPropsMap['object-timeline'] row declares them:
{
"type": "object-timeline",
"properties": {
"objectName": "project_task",
"timeline": { "startDateField": "start_date", "titleField": "name" },
"navigation": { "mode": "drawer" }
}
}The first record source present wins: items (authored entries, drawn as
written), then data (pre-fetched records), then a bind path, then the query
on objectName. Only the query is a fetch, so objectName is optional: a
timeline drawn from items, data or a bind path needs none to draw (entry-click
navigation still reads it, below), and a node-level dataSource binding can
supply the object instead. Records, from
data, a bind path or the query, are composed into entries through the field
bindings below; authored items are not.
| Prop | What the renderer does with it |
|---|---|
timeline |
Names the record fields an entry is drawn from: startDateField (its date), endDateField (the end printed after it), titleField, groupByField (the field whose value heads each group; without it, entries group into date buckets) and colorField (the marker colour: the field's option colour for the value, or the value itself when it is a colour literal). Each outranks the flat key of the same binding. scale sets the gantt axis unit. |
mapping |
{ title, date, description, variant } field names. title and date are read where the timeline block leaves the binding unset, ahead of the flat keys. variant names the field whose value picks the marker colour (the record's own variant field without it); on the vertical rail a colour from colorField outranks it. |
descriptionField |
The field an entry's description is read from (default description); mapping.description outranks it. |
limit |
The row cap, lowered to $top (default 100). A value that is not a positive integer is ignored with a console warning. |
filter / sort |
Lowered to $filter / $orderby on the query, so they do nothing when items or data supply the entries. The rail draws entries composed from records by start date, earliest first, whatever sort says. |
dateFormat |
short (the default) or long, both locale-formatted, or iso. |
rowLabel, minDate, maxDate |
The gantt's row-column header and its pinned axis start and end, read only by the gantt branch, which on this block draws authored items. An empty minDate / maxDate is not honoured; a value that is not a date, or a start after the end, refuses the chart with a diagnostic. |
navigation is what an entry click opens — the spec's NavigationConfig by
reference, ViewNavigationConfig in @object-ui/types, the type
ObjectGridSchema.navigation uses: mode (page, drawer, modal, split,
popover, new_window or none) with size, openNewTab and
preventNavigation.
| Member | On an entry click |
|---|---|
mode: 'drawer' / 'modal' / 'popover' |
Opens the entry's record in that overlay |
mode: 'new_window', or openNewTab: true |
Opens the record page in a new tab; openNewTab outranks every mode except none. On a timeline that names no objectName there is no record page: the tab opens at a slash and the entry's id alone (its _id when it has no id, undefined when it has neither) |
mode: 'page', or a block without mode (the spec's page default) |
Opens the record page of the timeline's objectName through the record navigator the host publishes (the console publishes one on its custom pages, record pages and list views); under a host that publishes none, such as an embedded renderer, or on a timeline that names no objectName, it opens nothing |
preventNavigation: true, or mode: 'none' |
Opens nothing, whatever else the block says |
size |
Sets the overlay width (sm … full, or auto for the default) |
A click handler from a parent view outranks the whole key.
With the key absent a click opens nothing: unlike the board and the calendar, this renderer supplies no drawer default. `split` opens nothing on this block either, because the timeline gives the split shell no main panel.Available marker/bar colors:
default- Graysuccess- Greeninfo- Bluewarning- Yellowdanger- Red
const item = {
time: '2024-01-15',
title: 'Important Milestone',
variant: 'success' // Green marker
}import type { TimelineSchema } from '@object-ui/types'
const roadmap: TimelineSchema = {
type: 'timeline',
variant: 'horizontal',
dateFormat: 'long',
items: [
{
time: '2024-01-01',
title: 'Alpha Release',
description: 'Internal testing phase',
variant: 'info',
icon: '🔬'
},
{
time: '2024-03-01',
title: 'Beta Launch',
description: 'Limited public beta',
variant: 'warning',
icon: '🚀'
},
{
time: '2024-06-01',
title: 'Public Launch',
description: 'General availability',
variant: 'success',
icon: '🎉'
}
]
}import type { TimelineSchema } from '@object-ui/types'
const schedule: TimelineSchema = {
type: 'timeline',
variant: 'gantt',
scale: 'week',
rowLabel: 'Teams',
items: [
{
label: 'Design Team',
items: [
{
title: 'Wireframes',
startDate: '2024-01-01',
endDate: '2024-01-14',
variant: 'info'
},
{
title: 'Mockups',
startDate: '2024-01-15',
endDate: '2024-01-31',
variant: 'success'
}
]
},
{
label: 'Engineering',
items: [
{
title: 'Development',
startDate: '2024-02-01',
endDate: '2024-03-31',
variant: 'warning'
}
]
}
]
}import type { TimelineSchema } from '@object-ui/types'
const history: TimelineSchema = {
type: 'timeline',
variant: 'vertical',
dateFormat: 'long',
items: [
{
time: '2020-01-01',
title: 'Company Founded',
description: 'Started in a garage with 2 founders',
variant: 'success',
icon: '🏢'
},
{
time: '2021-06-01',
title: 'Series A Funding',
description: 'Raised $5M from VCs',
variant: 'info',
icon: '💰'
},
{
time: '2022-12-01',
title: '10,000 Customers',
description: 'Reached major milestone',
variant: 'success',
icon: '🎯'
},
{
time: '2024-01-01',
title: 'Series B Funding',
description: 'Raised $20M to expand globally',
variant: 'warning',
icon: '🌍'
}
]
}The timeline component supports three date formats:
import type { TimelineSchema } from '@object-ui/types'
const short: TimelineSchema['dateFormat'] = 'short' // "1/15/2024"
const long: TimelineSchema['dateFormat'] = 'long' // "January 15, 2024"
const iso: TimelineSchema['dateFormat'] = 'iso' // "2024-01-15"A date-only value (2024-01-15) names a calendar day, so every face prints that day in every viewer's time zone, and the object timeline buckets and sorts it as that day. A value with a time part is an instant: short and long print its day in the viewer's zone, and iso prints its UTC day.
Without a groupByField, the object timeline groups entries into date buckets (Earlier, Today, Tomorrow, This week, Next week, Later, No date). "This week" starts on the first day of the week of the user's display locale, the day the calendar's weeks start on: Sunday under en-US, Monday under en-GB or zh-CN.
For Gantt-style timelines, choose the appropriate time scale:
import type { TimelineSchema } from '@object-ui/types'
const shortProject: TimelineSchema['scale'] = 'day' // short projects
const mediumProject: TimelineSchema['scale'] = 'week' // medium projects
const longProject: TimelineSchema['scale'] = 'month' // long projectsA date-only bar date, minDate or maxDate (2024-01-15) names a calendar day, so the axis headers, the bar positions and the bar tooltips all place it on that day in every viewer's time zone. A value with a time part is an instant and sits at its hour in the viewer's day. A plan with no bar dates and no pinned range shows a one-day axis on the viewer's today.
The headers and the bars share one axis. It runs from the start of the unit that holds the first date to the end of the unit that holds the last. Each header is as wide as its unit is long: hour, day and week columns are equal, and a month is as wide as its days. So a bar lines up under the header of the unit it starts in.
const item = {
time: '2024-01-15',
title: 'Milestone',
icon: '🎯', // Any emoji or short text
variant: 'success'
}Use Tailwind CSS classes for additional styling:
import type { TimelineSchema } from '@object-ui/types'
const schema: TimelineSchema = {
type: 'timeline',
variant: 'vertical',
className: 'max-w-4xl mx-auto',
items: [
{ time: '2024-01-15', title: 'Milestone', variant: 'success' }
]
}import type { TimelineSchema } from '@object-ui/types'
const timelineSchema: TimelineSchema = {
type: 'timeline',
variant: 'vertical',
items: [
{
time: '2024-01-15',
title: 'Event',
description: 'Description',
variant: 'success'
}
]
}