Timeline

Vertical chronological event feed for activity logs, audit trails, release notes, and notification streams. Pure semantic <ol>/<li> with a status-colored indicator and an optional icon override.

Installation

pnpm
pnpm dlx @hex-core/cli add timeline

Usage

tsx
import { Timeline } from "@/components/ui/timeline"

Activity log

Three-entry vertical feed with mixed status colors

tsx
import { Timeline } from "@/components/ui/timeline";

export function Example() {
  return (
    <Timeline
      aria-label="Activity"
      events={[
        { id: "1", title: "Pull request opened", timestamp: "2 hours ago", status: "info" },
        { id: "2", title: "CI passed", timestamp: "1 hour ago", status: "success" },
        { id: "3", title: "Merged to main", timestamp: "12 minutes ago", description: "Squash + merge by @oscar", status: "success" },
      ]}
    />
  );
}

Custom icon

Override the default dot with a custom node

tsx
import { Timeline } from "@/components/ui/timeline";

export function Example() {
  return (
    <Timeline
      aria-label="Release notes"
      events={[
        { id: "v1", title: "v1.0", timestamp: "Apr 24", icon: <span>⚡</span> },
        { id: "v2", title: "v1.1", timestamp: "Apr 27", icon: <span>🐛</span>, status: "warning" },
      ]}
    />
  );
}

API Reference

PropTypeDefaultDescription
eventsrequired
objectOrdered list of { id, title, timestamp?, description?, icon?, status? } events.
size
stringmdIndicator size: 'sm' | 'md'
aria-labelrequired
stringRequired accessible name for the ordered list (e.g. 'Activity log', 'Release notes')

AI Guidance

When to use

Use to show a chronological event feed: activity logs, audit trails, release notes, notification history, ticket events. Each event has a title and optional timestamp + description.

When not to use

Don't use for project schedules / Gantt charts (build a custom layout). Don't use for navigation between time periods (use Tabs or Stepper). Don't use for paginated data (use Table or DataTable). Don't use for >50 events without virtualization — Timeline renders every item.

Common mistakes

  • Forgetting aria-label — the <ol> needs an accessible name to be understood as a feed
  • Using duplicate event ids — breaks React keys and event reconciliation on re-render
  • Stuffing the description with rich layouts that overflow the timeline rail — keep it short or move to a Card
  • Setting status='error' on every event for emphasis — color loses meaning when overused
  • Mixing controlled timestamps as Date objects without formatting — Timeline accepts ReactNode, so format upstream (date-fns) before passing in

Accessibility

Renders <ol> with the provided aria-label. The status-colored indicator and connector line are aria-hidden — meaning is carried entirely by the title/timestamp/description text. No aria-current; events are historical, not navigational. For >50 events consider a windowing solution outside Timeline.

Related components

Token budget: 1100