Timeline
betaThings in time order on one rail: who did what, newest first, or the day's running order with a line at now.
Preview
activity
Sam Lee moved Planning to Room 3
Ava Patel added 4 people to the Design team
Invoice INV-0042 is due on Friday
Tom Hale signed the contract
The workspace moved to the Business plan
schedule
- 12:30
Doors open (done)
- 13:00
Welcome (done)
- 14:00
Keynote
- 14:40
Break
- 15:00
Workshop
- 16:30
Awards
no now
- 12:30
Doors open
- 13:00
Welcome
- 14:00
Keynote
limit
v2.12 release shipped
Slow checkout, Tom Hale
Fix merged, Mark Clarke
Sign-in errors, 12 users hit
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/timeline
Use it for
- layout="activity" for a feed of who did what, newest first. Pass person and avatar, and the name leads the line.
- layout="schedule" for the day's running order, with a time column. Pass now as "HH:MM" and keep it current.
- With now, done items go quiet, the one under way is bold, and a red line marks the moment between it and the next.
- Pass limit for a long feed. It shows that many, then a "Show N more" button that shows the rest in place.
- Give each item an id, and pass fresh on new ones. They fade up where they land, and the rest keep their place.
- Pass an icon for an item with no person, like an alert or a system change, and a tone to color its marker.
- Name the list with aria-label, like "Event day".
Avoid it for
- Steps people must finish in order. Use StepDots or a list of steps.
- Events on a calendar. Use Calendar.
- Items that are not in time order. Use a plain list.
- Long text in each item. Keep it to one line, with meta for a second.
Props
- items
Required. Each is { id?, text?, title?, meta?, time?, person?, avatar?, icon?, tone?, fresh? }. activity reads person and text. schedule reads title.
TimelineItem[]
- layout
activity: markers are photos or icons, and the time sits at the end. schedule: a time column on the left.
"activity" | "schedule" · default "activity"
- now
schedule: the current time as "HH:MM". It sets which items are done, which one is under way, and where the red line sits.
string
- limit
Show this many, then a "Show N more" button. Pressing it shows the rest and moves focus to the first new one.
number
- aria-label
Names the list for screen readers, like "Event day".
string
- TimelineItem.tone
The marker's color: the dot's ring, or the icon's soft fill.
"neutral" | "brand" | "danger" | "success" | "warning" · default "neutral"
- TimelineItem.avatar
activity: the person's photo, as the marker. Pass an avatar 28px across.
ReactNode
- TimelineItem.icon
The marker when there is no avatar, like a lucide icon.
ReactNode
- TimelineItem.fresh
Just arrived. It fades up where it lands, once.
boolean
- className
Extra classes on the outer box. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| items | TimelineItem[] | - | Required. Each is { id?, text?, title?, meta?, time?, person?, avatar?, icon?, tone?, fresh? }. activity reads person and text. schedule reads title. |
| layout | "activity" | "schedule" | "activity" | activity: markers are photos or icons, and the time sits at the end. schedule: a time column on the left. |
| now | string | - | schedule: the current time as "HH:MM". It sets which items are done, which one is under way, and where the red line sits. |
| limit | number | - | Show this many, then a "Show N more" button. Pressing it shows the rest and moves focus to the first new one. |
| aria-label | string | - | Names the list for screen readers, like "Event day". |
| TimelineItem.tone | "neutral" | "brand" | "danger" | "success" | "warning" | "neutral" | The marker's color: the dot's ring, or the icon's soft fill. |
| TimelineItem.avatar | ReactNode | - | activity: the person's photo, as the marker. Pass an avatar 28px across. |
| TimelineItem.icon | ReactNode | - | The marker when there is no avatar, like a lucide icon. |
| TimelineItem.fresh | boolean | - | Just arrived. It fades up where it lands, once. |
| className | string | - | Extra classes on the outer box. Later classes win. |
Accessibility
- The items are an <ol>, so screen readers say how many there are and where each one is.
- Name the list with aria-label. The rail and the markers are hidden from screen readers.
- In a schedule with now, the item under way carries aria-current="time", and done items end with a hidden "(done)".
- The red now line is drawn only. aria-current carries the same news for screen readers.
- "Show N more" is a real button. Once pressed, it goes away and focus moves to the first item it showed.
- Text is the foreground or muted-foreground color, which the theme tests hold to 4.5:1 on the page and on cards, in light and dark.
Examples
Who did what
<Timeline
aria-label="Recent activity"
items={[
{ id: "a3", person: "Sam Lee", text: "moved Planning to Room 3", time: "18:20" },
{ id: "a2", person: "Ava Patel", text: "added 4 people to the Design team", time: "17:05" },
{ id: "a1", text: "Invoice INV-0042 is due", time: "Mon", tone: "warning" },
]}
/>The day's running order
<Timeline
layout="schedule"
now="14:40"
aria-label="Event day"
items={[
{ time: "13:00", title: "Welcome", meta: "Room 2" },
{ time: "14:00", title: "Keynote" },
{ time: "15:00", title: "Workshop" },
{ time: "16:30", title: "Awards", meta: "Boardroom" },
]}
/>A long feed
<Timeline aria-label="Team news" limit={5} items={news} />