Timeline

Follow what happened, newest first, grouped by day.

datafeedactivity
Preview
Timeline preview
Today, 2 updates
  1. Elia merged Manicat v2 into mainPR #482
  2. Deploy bot deployed to stagingcommit 9f3ca2b
Yesterday, 2 updates
  1. Mara opened a PR for the search dialogPR #491
  2. CI failed on mainRun #12041

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
Today, 2 updates
  1. Elia merged the redesign into mainPR #482
  2. Deploy bot deployed to stagingcommit 9f3ca2b
Yesterday, 1 update
  1. CI failed on mainRun #12041

Keyboard interactions

KeysAction
ArrowDown / ArrowUpMoves between expandable rows.
Home / EndJumps to the first or last expandable row.
Enter / SpaceExpands 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.