Skip to main content

Timeline

The Timeline component provides a clean, accessible way to display events in chronological order. Supports both vertical and horizontal layouts with customizable markers, titles, and descriptions.

Installation

npm install @craftdotui/components

Import

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineTitle,
  TimelineDescription,
} from "@craftdotui/components";

Usage

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineTitle,
  TimelineDescription,
} from "@craftdotui/components";

export default function Example() {
  return (
    <Timeline>
      <TimelineItem>
        <TimelineMarker variant="fill" />
        <TimelineContent>
          <TimelineTitle>Project Started</TimelineTitle>
          <TimelineDescription>
            Initial planning and requirements gathering
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
      
      <TimelineItem>
        <TimelineMarker variant="fill" />
        <TimelineContent>
          <TimelineTitle>Development Phase</TimelineTitle>
          <TimelineDescription>
            Building core features and functionality
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
      
      <TimelineItem>
        <TimelineMarker variant="empty" />
        <TimelineContent>
          <TimelineTitle>Launch</TimelineTitle>
          <TimelineDescription>
            Public release scheduled
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
    </Timeline>
  );
}

Components

Timeline (Root)

children
React.ReactNode
required
Timeline items to display.
variant
'vertical' | 'horizontal'
default:"'vertical'"
Layout orientation of the timeline.
className
string
Additional CSS classes for the timeline container.
aria-label
string
default:"'Timeline'"
Accessible label for screen readers.

TimelineItem

children
React.ReactNode
required
Timeline item content (marker and content components).
className
string
Additional CSS classes for the item.

TimelineMarker

variant
'empty' | 'fill'
default:"'empty'"
Visual style of the marker.
className
string
Additional CSS classes for custom marker styling.

TimelineContent

children
React.ReactNode
required
Content to display for this timeline item.
className
string
Additional CSS classes.

TimelineTitle

children
React.ReactNode
required
Title text for the timeline item.
className
string
Additional CSS classes.

TimelineDescription

children
React.ReactNode
required
Description text for the timeline item.
className
string
Additional CSS classes.

Styling Details

Connecting Line

The timeline automatically renders a connecting line:
  • Vertical: Left-aligned, full height (left-1.5 top-0 w-px h-full)
  • Horizontal: Top-aligned, full width (top-1.5 left-0 h-px w-full)

Marker Variants

// Empty (outline only)
variant="empty" // bg-white dark:bg-neutral-900

// Filled
variant="fill" // bg-neutral-300 dark:bg-neutral-600
Markers use relative z-10 to appear above the connecting line.

Examples

Product Roadmap

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineTitle,
  TimelineDescription,
} from "@craftdotui/components";

export default function ProductRoadmap() {
  const milestones = [
    {
      title: "Q1 2024 - Launch",
      description: "Initial product release with core features",
      completed: true,
    },
    {
      title: "Q2 2024 - Mobile App",
      description: "iOS and Android applications",
      completed: true,
    },
    {
      title: "Q3 2024 - AI Features",
      description: "Machine learning powered recommendations",
      completed: false,
    },
    {
      title: "Q4 2024 - Enterprise",
      description: "Advanced team collaboration tools",
      completed: false,
    },
  ];

  return (
    <div className="max-w-2xl mx-auto p-8">
      <h2 className="text-3xl font-bold mb-8">Product Roadmap</h2>
      <Timeline>
        {milestones.map((milestone, i) => (
          <TimelineItem key={i}>
            <TimelineMarker 
              variant={milestone.completed ? "fill" : "empty"}
              className={milestone.completed ? "bg-blue-500 border-blue-600" : ""}
            />
            <TimelineContent>
              <TimelineTitle className={milestone.completed ? "text-blue-600" : ""}>
                {milestone.title}
              </TimelineTitle>
              <TimelineDescription>
                {milestone.description}
              </TimelineDescription>
            </TimelineContent>
          </TimelineItem>
        ))}
      </Timeline>
    </div>
  );
}

Order Tracking

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineTitle,
  TimelineDescription,
} from "@craftdotui/components";
import { Package, Truck, Check } from "lucide-react";

export default function OrderTracking() {
  return (
    <Timeline>
      <TimelineItem>
        <TimelineMarker className="bg-green-500 border-green-600">
          <Check className="w-2 h-2 text-white" />
        </TimelineMarker>
        <TimelineContent>
          <TimelineTitle>Order Placed</TimelineTitle>
          <TimelineDescription>
            Dec 15, 2024 at 10:30 AM
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
      
      <TimelineItem>
        <TimelineMarker className="bg-green-500 border-green-600">
          <Package className="w-2 h-2 text-white" />
        </TimelineMarker>
        <TimelineContent>
          <TimelineTitle>Processing</TimelineTitle>
          <TimelineDescription>
            Dec 15, 2024 at 2:00 PM
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
      
      <TimelineItem>
        <TimelineMarker className="bg-blue-500 border-blue-600 animate-pulse">
          <Truck className="w-2 h-2 text-white" />
        </TimelineMarker>
        <TimelineContent>
          <TimelineTitle>In Transit</TimelineTitle>
          <TimelineDescription>
            Expected delivery: Dec 18, 2024
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
      
      <TimelineItem>
        <TimelineMarker variant="empty" />
        <TimelineContent>
          <TimelineTitle className="text-gray-400">Delivered</TimelineTitle>
          <TimelineDescription className="text-gray-400">
            Pending
          </TimelineDescription>
        </TimelineContent>
      </TimelineItem>
    </Timeline>
  );
}

Career History

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineTitle,
  TimelineDescription,
} from "@craftdotui/components";

export default function CareerHistory() {
  const jobs = [
    {
      title: "Senior Developer",
      company: "Tech Corp",
      period: "2022 - Present",
      description: "Leading frontend team, architecting scalable solutions",
    },
    {
      title: "Full Stack Developer",
      company: "StartupXYZ",
      period: "2020 - 2022",
      description: "Built core product features, grew user base to 100k",
    },
    {
      title: "Junior Developer",
      company: "Web Agency",
      period: "2018 - 2020",
      description: "Developed client websites and web applications",
    },
  ];

  return (
    <Timeline className="max-w-3xl">
      {jobs.map((job, i) => (
        <TimelineItem key={i}>
          <TimelineMarker variant={i === 0 ? "fill" : "empty"} />
          <TimelineContent>
            <div className="flex justify-between items-start mb-1">
              <TimelineTitle>{job.title}</TimelineTitle>
              <span className="text-sm text-gray-500">{job.period}</span>
            </div>
            <p className="text-sm font-medium text-gray-600 mb-1">{job.company}</p>
            <TimelineDescription>{job.description}</TimelineDescription>
          </TimelineContent>
        </TimelineItem>
      ))}
    </Timeline>
  );
}

Project Phases (Horizontal)

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineTitle,
} from "@craftdotui/components";

export default function ProjectPhases() {
  const phases = ["Discovery", "Design", "Development", "Testing", "Launch"];
  const currentPhase = 2;

  return (
    <div className="w-full">
      <Timeline variant="horizontal" className="py-12">
        {phases.map((phase, i) => (
          <TimelineItem key={i}>
            <TimelineMarker 
              variant={i <= currentPhase ? "fill" : "empty"}
              className={i === currentPhase ? "bg-blue-500 border-blue-600 scale-125" : ""}
            />
            <TimelineContent>
              <TimelineTitle className={i <= currentPhase ? "text-blue-600 font-bold" : "text-gray-400"}>
                {phase}
              </TimelineTitle>
            </TimelineContent>
          </TimelineItem>
        ))}
      </Timeline>
    </div>
  );
}
Use different marker colors to represent status (green for complete, blue for current, gray for pending).

Hook: useTimeline

Access timeline context from child components:
import { useTimeline } from "@craftdotui/components";

function CustomTimelineItem() {
  const { variant } = useTimeline();
  
  return (
    <div>
      Layout: {variant}
    </div>
  );
}

Best Practices

  • Use filled markers for completed items, empty for upcoming
  • Keep descriptions concise (1-2 lines)
  • Include timestamps or dates for clarity
  • Use consistent marker styling for similar states
  • Limit vertical timelines to 6-8 items for readability
  • For horizontal layouts, ensure responsive behavior on mobile

Accessibility

  • Uses semantic <ul> and <li> elements
  • Includes aria-label for screen readers
  • Markers use aria-hidden="true" as they’re decorative
  • Proper heading hierarchy with <h3> for titles
  • High contrast text colors for readability

Build docs developers (and LLMs) love