Timeline
Display events with shared fields or configure each event.
Use timeline(data) for events with the same fields. Use
timeline(data, callback) for events that need different fields or controls.
The data order is the display order. Sort the records in the resolver.
Record timeline
import {
defineResource,
fields,
timeline,
} from '@backlit/sdk'
const orders = defineResource('orders', [
fields.date('at'),
fields.text('title'),
fields.text('body', { format: 'markdown' }),
])
const rows = [
{
at: '2026-09-01T10:00:00Z',
title: 'Order received',
body: '**Paid** in full.',
},
]
orders.view('activity', () =>
timeline(rows).setFields({
time: orders.get('at'),
title: orders.get('title'),
})
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "activity",
"slots": {
"content": [
{
"kind": "record-timeline",
"fields": {
"time": [
{
"kind": "date",
"name": "at",
"label": ""
}
],
"title": [
{
"kind": "text",
"name": "title",
"label": ""
}
]
},
"data": [
{
"at": "2026-09-01T10:00:00.000Z",
"title": "Order received"
}
]
}
]
}
}
}setFields accepts marker, time, title, description, meta, and body.
time and title are required. marker, time, and body accept one field
each. meta accepts at most four fields. title and description accept one
field or an array. Calling setFields replaces all positions.
Use onRowClick for a link, setRowActions for controls, and onDefect for
conversion failures. setDensity('compact') omits description and body.
Record timelines support setPaginator and setSorter with bindings from one
scope. The resolver applies their values to the query.
Item timeline
import {
defineResource,
fields,
timeline,
} from '@backlit/sdk'
const orders = defineResource('orders', [
fields.date('at'),
fields.text('title'),
fields.text('body', { format: 'markdown' }),
])
const rows = [
{
at: '2026-09-01T10:00:00Z',
title: 'Order received',
body: '**Paid** in full.',
},
]
orders.view('activity', () =>
timeline(rows, (item, row) => {
item.setFields({
time: orders.get('at'),
title: orders.get('title'),
body: row.body ? orders.get('body') : undefined,
})
})
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "activity",
"slots": {
"content": [
{
"kind": "item-timeline",
"data": [
{
"time": [
{
"field": {
"kind": "date",
"name": "at",
"label": ""
},
"value": "2026-09-01T10:00:00.000Z"
}
],
"title": [
{
"field": {
"kind": "text",
"name": "title",
"label": ""
},
"value": "Order received"
}
],
"body": [
{
"field": {
"kind": "text",
"name": "body",
"label": "",
"format": "markdown"
},
"value": "**Paid** in full."
}
]
}
]
}
]
}
}
}The callback receives a fresh TimelineItemBuilder and the event record.
Use setFields, onClick, and setActions for that event. The same position
limits apply. The item timeline supports setDensity, onDefect, and
setPaginator. It has no shared sorter. See Search state.