Skip to content
goodtabs UI
BrowseBrowse components

Timeline

Vertical dated events on one hairline, newest first.

npx shadcn@latest add https://ui.goodtabs.co/r/timeline.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/timeline

  1. Dark theme shipped

  2. Registry opened

  3. First sketch

Readable at 250px, dates in mono.

With detail

  1. Version 1.4 released

    Forty new components, including data display and feedback.

  2. Registry installs passed 1,000

    Most installs came from the form and navigation sets.

  3. Switched to the new palette

    Near-black surfaces, one indigo accent, hairline borders.

A quiet line under each title, inside a bordered box.

Usage

import { Timeline, TimelineItem } from "@/components/ui/timeline"

<Timeline>
  <TimelineItem title="Registry opened" date="22 Oct" dateTime="2026-10-22" />
</Timeline>

Props

PropTypeDefaultDescription
titleReactNodeTimelineItem: what happened.
datestringTimelineItem: the date as people read it.
dateTimestringTimelineItem: machine-readable date for the time element.
descriptionstringTimelineItem: one quiet line of detail.

Source

timeline/timeline.tsx
import * as React from "react";
import { cn } from "@/lib/utils";

type TimelineProps = React.ComponentProps<"ol">;

/** A vertical list of dated events on one hairline. */
export function Timeline({ className, ...props }: TimelineProps) {
  return <ol className={cn("m-0 flex list-none flex-col p-0", className)} {...props} />;
}

type TimelineItemProps = Omit<React.ComponentProps<"li">, "title"> & {
  /** What happened, one line. */
  title: React.ReactNode;
  /** A machine-readable date, for example 2026-10-29. */
  dateTime: string;
  /** The date as people read it, for example "29 Oct". */
  date: string;
  /** One quiet line of detail. */
  description?: string;
};

export function TimelineItem({ title, dateTime, date, description, className, ...props }: TimelineItemProps) {
  return (
    <li
      className={cn(
        "relative grid grid-cols-[1rem_1fr] gap-x-3 pb-5 last:pb-0",
        "before:absolute before:top-3 before:bottom-0 before:left-[calc(0.5rem-0.5px)] before:w-px before:bg-border last:before:hidden",
        className,
      )}
      {...props}
    >
      <span
        aria-hidden="true"
        className="mt-1.5 size-2 place-self-start justify-self-center rounded-full border-2 border-foreground bg-background"
      />
      <div className="flex min-w-0 flex-col gap-0.5">
        <p className="m-0 text-sm text-foreground">{title}</p>
        <time dateTime={dateTime} className="font-mono text-xs text-muted-foreground">
          {date}
        </time>
        {description && <p className="m-0 mt-1 text-sm text-muted-foreground">{description}</p>}
      </div>
    </li>
  );
}

For AI agents

Hand your agent this page, the markdown at /components/timeline/prompt.md, or the Copy AI prompt button above.

When to use

  • Changelogs, project history, order or build history
  • A short run of dated events

When not to use

  • Who did what: use activity-feed
  • Steps in a flow: use stepper

Agent notes

  • Pass items in the order to show; it does not sort
  • The line stops at the last item
  • Renders an ol, so screen readers announce the count