Timeline
ProAn activity feed with avatars or icons, actor, action, target and relative time, optional comments and attachments, grouped by day with sticky labels and a connector line.
npx prodular-ui add timelinePart of Prodular UI Pro. Preview and read the docs freely, install with a license. See pricing
Account activity
Grouped by day inside a scrolling card. Day labels stick to the top while their group scrolls past.
Activity
Northwind Logistics
Today
- MO
Maya Okafor commented on INV-2094
Their AP team confirmed the PO number. Payment is scheduled for Friday, so let us hold the reminder. INV-2088 was paid, $18,420.00 by ACH
- TL
Theo Lindqvist attached the renewal quote
northwind-renewal-2027.pdf284 KB
Yesterday
Ledgerline sent a payment reminder for INV-2094
Priya Raman added Jonas Becker as billing contact
Oct 16
Salesforce sync updated the plan to Scale, 240 seats
- DF
Daniel Ferreira marked the account as Expansion opportunity
import { CreditCardIcon, FileTextIcon, MailIcon, RefreshCwIcon, UserPlusIcon } from "lucide-react"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"import { Timeline, TimelineActor, TimelineAttachment, TimelineBody, TimelineContent, TimelineGroup, TimelineGroupLabel, TimelineHeader, TimelineItem, TimelineList, TimelineMedia, TimelineTarget, TimelineTime, TimelineTitle,} from "@/components/ui/timeline"
// A fixed "now" keeps server and client output identical.const now = new Date(2026, 9, 21, 15, 40)const at = (d: number, h: number, m: number) => new Date(2026, 9, d, h, m)
export default function TimelineActivity() { return ( <div className="w-full max-w-xl rounded-xl border bg-card shadow-xs"> <div className="border-b px-5 py-3.5"> <p className="text-sm font-medium">Activity</p> <p className="text-[13px] text-muted-foreground">Northwind Logistics</p> </div> <div className="max-h-96 overflow-y-auto px-5 pb-3"> <Timeline className="[--timeline-surface:var(--card)]"> <TimelineGroup> <TimelineGroupLabel>Today</TimelineGroupLabel> <TimelineList> <TimelineItem> <TimelineMedia variant="avatar"> <Avatar size="sm"> <AvatarFallback>MO</AvatarFallback> </Avatar> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Maya Okafor</TimelineActor> commented on <TimelineTarget>INV-2094</TimelineTarget> </TimelineTitle> <TimelineTime dateTime={at(21, 15, 12)} now={now} /> </TimelineHeader> <TimelineBody> Their AP team confirmed the PO number. Payment is scheduled for Friday, so let us hold the reminder. </TimelineBody> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia variant="success"> <CreditCardIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineTarget>INV-2088</TimelineTarget> was paid, $18,420.00 by ACH </TimelineTitle> <TimelineTime dateTime={at(21, 11, 2)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia variant="avatar"> <Avatar size="sm"> <AvatarFallback>TL</AvatarFallback> </Avatar> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Theo Lindqvist</TimelineActor> attached the renewal quote </TimelineTitle> <TimelineTime dateTime={at(21, 9, 48)} now={now} /> </TimelineHeader> <TimelineAttachment> <FileTextIcon /> <span className="truncate font-medium">northwind-renewal-2027.pdf</span> <span className="text-muted-foreground tabular-nums">284 KB</span> </TimelineAttachment> </TimelineContent> </TimelineItem> </TimelineList> </TimelineGroup> <TimelineGroup> <TimelineGroupLabel>Yesterday</TimelineGroupLabel> <TimelineList> <TimelineItem> <TimelineMedia> <MailIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Ledgerline</TimelineActor> sent a payment reminder for <TimelineTarget>INV-2094</TimelineTarget> </TimelineTitle> <TimelineTime dateTime={at(20, 16, 30)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia variant="brand"> <UserPlusIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Priya Raman</TimelineActor> added <TimelineTarget>Jonas Becker</TimelineTarget> as billing contact </TimelineTitle> <TimelineTime dateTime={at(20, 10, 5)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> </TimelineList> </TimelineGroup> <TimelineGroup> <TimelineGroupLabel>Oct 16</TimelineGroupLabel> <TimelineList> <TimelineItem> <TimelineMedia> <RefreshCwIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Salesforce sync</TimelineActor> updated the plan to <TimelineTarget>Scale, 240 seats</TimelineTarget> </TimelineTitle> <TimelineTime dateTime={at(16, 14, 20)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia variant="avatar"> <Avatar size="sm"> <AvatarFallback>DF</AvatarFallback> </Avatar> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Daniel Ferreira</TimelineActor> marked the account as <TimelineTarget>Expansion opportunity</TimelineTarget> </TimelineTitle> <TimelineTime dateTime={at(16, 9, 0)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> </TimelineList> </TimelineGroup> </Timeline> </div> </div> )}Deploy feed
Tinted icons for outcomes, a muted icon for neutral events.
api-gateway deployed to production
a41f9c2 · Rate limit per workspace
billing-worker failed health checks
3 of 6 pods exited with OOMKilled. Traffic stayed on the previous release.Hana Sato rolled back billing-worker
Theo Lindqvist merged #1287 into main
import { CheckIcon, GitBranchIcon, RotateCcwIcon, XIcon } from "lucide-react"
import { Timeline, TimelineActor, TimelineBody, TimelineContent, TimelineHeader, TimelineItem, TimelineList, TimelineMedia, TimelineTarget, TimelineTime, TimelineTitle,} from "@/components/ui/timeline"
const now = new Date(2026, 9, 21, 15, 40)
export default function TimelineDeploys() { return ( <Timeline className="max-w-md"> <TimelineList> <TimelineItem> <TimelineMedia variant="success"> <CheckIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineTarget>api-gateway</TimelineTarget> deployed to production </TimelineTitle> <TimelineTime dateTime={new Date(2026, 9, 21, 15, 34)} now={now} /> </TimelineHeader> <p className="font-mono text-[13px] text-muted-foreground">a41f9c2 · Rate limit per workspace</p> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia variant="destructive"> <XIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineTarget>billing-worker</TimelineTarget> failed health checks </TimelineTitle> <TimelineTime dateTime={new Date(2026, 9, 21, 14, 2)} now={now} /> </TimelineHeader> <TimelineBody className="text-muted-foreground"> 3 of 6 pods exited with OOMKilled. Traffic stayed on the previous release. </TimelineBody> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia variant="warning"> <RotateCcwIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Hana Sato</TimelineActor> rolled back <TimelineTarget>billing-worker</TimelineTarget> </TimelineTitle> <TimelineTime dateTime={new Date(2026, 9, 21, 13, 55)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> <TimelineItem> <TimelineMedia> <GitBranchIcon /> </TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Theo Lindqvist</TimelineActor> merged <TimelineTarget>#1287</TimelineTarget> into main </TimelineTitle> <TimelineTime dateTime={new Date(2026, 9, 21, 13, 31)} now={now} /> </TimelineHeader> </TimelineContent> </TimelineItem> </TimelineList> </Timeline> )}Anatomy
<Timeline> <TimelineGroup> <TimelineGroupLabel>Today</TimelineGroupLabel> <TimelineList> <TimelineItem> <TimelineMedia variant="avatar"><Avatar size="sm">...</Avatar></TimelineMedia> <TimelineContent> <TimelineHeader> <TimelineTitle> <TimelineActor>Maya Okafor</TimelineActor> commented on <TimelineTarget>INV-2094</TimelineTarget> </TimelineTitle> <TimelineTime dateTime={event.at} /> </TimelineHeader> <TimelineBody>Payment is scheduled for Friday.</TimelineBody> </TimelineContent> </TimelineItem> </TimelineList> </TimelineGroup></Timeline>Guidance
Use it when
- The history of a record: who changed what and when. Accounts, invoices, tickets, deploys.
- System and human events in one stream, with avatars for people and icons for systems.
Avoid
- Comment threads people reply to. Use a chat or comments component.
- Wrapping comments in tinted bubbles inside a card. TimelineBody uses a hairline.
- Tinting every icon. Reserve success, warning and destructive for outcomes.
Rules for AI agents
Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.
- Write each title as actor, action, target: wrap the person or system in TimelineActor and the object in TimelineTarget, keep the verb plain.
- Use TimelineMedia variant="avatar" with an Avatar size="sm" for people, variant="icon" for neutral system events, and success, warning, destructive or info only for outcomes.
- Pass dateTime to TimelineTime. Pass a fixed now when rendering on the server so relative times match after hydration.
- Group by calendar day with TimelineGroup and TimelineGroupLabel (Today, Yesterday, then dates). Set --timeline-surface to the background the timeline sits on, for example [--timeline-surface:var(--card)] inside a card, so sticky labels cover the content.
- List newest first.
API
TimelineMedia
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "avatar" | "icon" | "success" | "warning" | "destructive" | "info" | "brand" | "icon" | The circle to the left of each event. |
TimelineTime
| Prop | Type | Default | Description |
|---|---|---|---|
| dateTime | Date | string | number | When it happened. Shown relative to now unless children are passed. | |
| now | Date | number | Date.now() | Reference time for the relative label. |
TimelineGroupLabel
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | The day. Sticky inside the nearest scroll container. |
Timeline
| Prop | Type | Default | Description |
|---|---|---|---|
| --timeline-surface | CSS variable | var(--background) | Background of the sticky day labels. |
formatRelativeTime
| Prop | Type | Default | Description |
|---|---|---|---|
| (date, now) | function | The formatter TimelineTime uses, exported for other places. |
Installation
The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.
npx prodular-ui add timeline# or, with the registry in components.jsonnpx shadcn@latest add @prodular/timeline