Timeline
Follow what happened, newest first, grouped by day.
datafeedactivity
Preview
Timeline preview
Live · keyboard ready
Today, 2 updates
- Elia merged Manicat v2 into main
- Deploy bot deployed to staging
Yesterday, 2 updates
- Mara opened a PR for the search dialog
- CI failed on main
When to use
- Project history, audit logs, and deploy streams where recency matters.
- Live feeds where new updates slide in at the top.
- Rows that expand in place to show logs or detail.
When not to use
- Use sortable-data-table when people sort or compare.
- Use stepper for progress through fixed steps.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "timeline",
"type": "registry:ui",
"title": "Timeline",
"description": "Follow what happened, newest first, grouped by day.",
"categories": [
"data"
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/timeline/timeline.tsx",
"type": "registry:component",
"target": "@components/manicat/timeline/timeline.tsx"
},
{
"path": "registry/components/timeline/timeline.module.css",
"type": "registry:component",
"target": "@components/manicat/timeline/timeline.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/timeline",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/timeline",
"markdown": "https://uiarc.dev/components/timeline/markdown",
"tags": [
"data",
"feed",
"activity"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { Timeline } from "@/components/manicat/timeline/timeline";
import type { TimelineEvent } from "@/components/manicat/timeline/timeline";
const now = Date.UTC(2026, 8, 9, 12);
const timelineEvents: TimelineEvent[] = [
{ id: "m1", at: now - 2 * 3_600_000, actor: "Elia", title: "merged Manicat v2 into main", meta: "PR #482" },
{ id: "m2", at: now - 5 * 3_600_000, actor: "Deploy bot", title: "deployed to staging", meta: "commit 9f3ca2b", tone: "success" },
{ id: "m3", at: now - 26 * 3_600_000, actor: "Mara", title: "opened a PR for the search dialog", meta: "PR #491" },
{ id: "m4", at: now - 30 * 3_600_000, actor: "CI", title: "failed on main", meta: "Run #12041", tone: "danger" },
];
export function TimelineExample() {
return <Timeline events={timelineEvents} now={now} label="Project activity" timeZone="UTC" />;
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Activity feed Who did what and when, with relative times and a tone for outcomes.
Activity feed
Who did what and when, with relative times and a tone for outcomes. preview
Live · keyboard ready
Today, 2 updates
- Elia merged the redesign into main
- Deploy bot deployed to staging
Yesterday, 1 update
- CI failed on main
activity-feed-who-did-what-and-when-with-relative-times-and-a-tone-for-outcomes-.tsxtsx
"use client";
import { Timeline } from "@/components/manicat/timeline/timeline";
import type { TimelineEvent } from "@/components/manicat/timeline/timeline";
const now = Date.UTC(2026, 8, 9, 12);
const events: TimelineEvent[] = [
{ id: "e1", at: now - 2 * 3_600_000, actor: "Elia", title: "merged the redesign into main", meta: "PR #482" },
{ id: "e2", at: now - 5 * 3_600_000, actor: "Deploy bot", title: "deployed to staging", meta: "commit 9f3ca2b", tone: "success" },
{ id: "e3", at: now - 26 * 3_600_000, actor: "CI", title: "failed on main", meta: "Run #12041", tone: "danger" },
];
export function TimelineActivityExample() {
return <Timeline events={events} now={now} label="Project activity" timeZone="UTC" />;
}
Keyboard interactions
| Keys | Action |
|---|---|
| ArrowDown / ArrowUp | Moves between expandable rows. |
| Home / End | Jumps to the first or last expandable row. |
| Enter / Space | Expands or collapses the focused row. |
Accessibility
- The feed is a labelled role="region"; day labels are headings at headingLevel with a hidden update count.
- Expandable rows are buttons with aria-expanded and aria-controls; rows without detail are not interactive.
- Times use a time element with the full date in hidden text, and new updates are announced in a status region.
- Say the outcome in the title, since tone is color only.
Motion
- The connecting line draws as rows come into view and markers pop in.
- New updates slide in at the top while the rest glide down; details expand on a spring.
- Reduced motion replaces travel with short fades and instant height changes.
Responsive behavior
- Day labels stay pinned while updates scroll under them, inside maxHeight or the page.
- Below 420px row padding tightens so text keeps its width.
Performance
- Events are not virtualized; set maxHeight for long feeds and trim old events.
- Pass a now that ticks about once a minute rather than every second, since it re-renders every row.
Notes for AI
- Use for project history, audit logs, and deploy streams where order and recency matter. Use sortable-data-table when people sort or compare.
- Pass a ticking now (for example updated every minute) so relative times stay current, and a fixed timeZone to avoid hydration mismatches.