Timeline

A flexible scroll-driven timeline with animated progress, configurable orientations, customizable pins, and optional external scroll-container control.

Made by raouf.codes
Edit on GitHub

Overview

Core Concept

The Timeline component provides a scroll-driven timeline experience with support for both horizontal and vertical orientations. Built with Framer Motion's scroll-linked animations, it features animated progress bars, customizable pins, and smooth transitions.

Key Features

  • Scroll-driven progress — Progress bar animates based on scroll position
  • Responsive orientation — Automatically switches to vertical on mobile
  • Animated pins — Scale and opacity transitions as items enter viewport
  • Sticky viewport — Horizontal mode keeps timeline fixed during scroll
  • RTL support — Full right-to-left layout via the direction prop
  • External scroll source — Drive timeline animation from a separate scrolling container via containerScrollRef

Anatomy

  • Timeline — Root component (orientation resolver)
  • Timeline.Item — Individual timeline entry with title and content
  • Timeline.Header — Optional header block for the timeline

By default, Timeline automatically switches to vertical layout on mobile screens.

Installation

Usage

Basic

import { Timeline } from '@/components/azemmur/components/timeline';

export default function Example() {
  return (
    <Timeline>
      <Timeline.Item title="2020">
        <div>First milestone</div>
      </Timeline.Item>
      <Timeline.Item title="2021">
        <div>Second milestone</div>
      </Timeline.Item>
      <Timeline.Item title="2022">
        <div>Third milestone</div>
      </Timeline.Item>
    </Timeline>
  );
}

Vertical Orientation

<Timeline orientation="vertical">
  <Timeline.Item title="Step 1">
    <p>First step content</p>
  </Timeline.Item>
  <Timeline.Item title="Step 2">
    <p>Second step content</p>
  </Timeline.Item>
</Timeline>

Separate Components

Use dedicated components when you want an explicit layout regardless of viewport:

<Timeline.Horizontal>
  <Timeline.Item title="Step 1">
    <p>Horizontal content</p>
  </Timeline.Item>
</Timeline.Horizontal>

<Timeline.Vertical>
  <Timeline.Item title="Step 1">
    <p>Vertical content</p>
  </Timeline.Item>
</Timeline.Vertical>

Composition with an External Scroll Container

Use containerScrollRef when a parent container should control timeline progress (for example, inside a scrollable panel).

import { useRef } from 'react';
import { Timeline } from '@/components/azemmur/components/timeline';

export function TimelineInScrollPanel() {
  const panelRef = useRef<HTMLDivElement>(null);

  return (
    <div ref={panelRef} className="h-[520px] overflow-y-auto">
      <Timeline orientation="vertical" containerScrollRef={panelRef}>
        <Timeline.Item title="2024">
          <p>Milestone content</p>
        </Timeline.Item>
        <Timeline.Item title="2025">
          <p>Next milestone</p>
        </Timeline.Item>
      </Timeline>
    </div>
  );
}

With Header

<Timeline intent="primary">
  <Timeline.Header>
    <h2 className="text-4xl font-bold">My Journey</h2>
  </Timeline.Header>

  <Timeline.Item title="2020">
    <div>First milestone</div>
  </Timeline.Item>
</Timeline>

Variant Examples

Size Variants

<Timeline size="sm">...</Timeline>
<Timeline size="md">...</Timeline>
<Timeline size="lg">...</Timeline>

Gradient Variants

<Timeline gradient="purple-blue">...</Timeline>
<Timeline gradient="orange-red">...</Timeline>
<Timeline gradient="green-teal">...</Timeline>
<Timeline gradient="rainbow">...</Timeline>
<Timeline gradient="none">...</Timeline>

Visual Styles

<Timeline visuals="card">...</Timeline>
<Timeline visuals="bordered">...</Timeline>
<Timeline visuals="solid">...</Timeline>
<Timeline visuals="dashed">...</Timeline>
<Timeline visuals="dotted">...</Timeline>

Pin Shapes

<Timeline shape="circle">...</Timeline>
<Timeline shape="ring">...</Timeline>
<Timeline shape="dot">...</Timeline>
<Timeline shape="square">...</Timeline>
<Timeline shape="diamond">...</Timeline>

Mobile Behavior

Timeline switches from horizontal to vertical on mobile by default. You can disable this behavior with responsive={false}.

import { Timeline } from '@/components/azemmur/components/timeline';

export function TimelineExample() {
  return (
    <Timeline orientation="horizontal" responsive={false}>
      <Timeline.Item title="2020">
        <p>Content</p>
      </Timeline.Item>
    </Timeline>
  );
}

Direction (LTR / RTL)

Use the direction prop to control reading direction:

<Timeline direction="rtl">
  <Timeline.Item title="2020">
    <div>Content</div>
  </Timeline.Item>
</Timeline>

API Reference

Timeline

PropTypeDefault
children
React.ReactNode
-
orientation?
enum
"horizontal"
responsive?
boolean
true
mobileBreakpoint?
number
1024
containerScrollRef?
React.RefObject<HTMLElement | null>
-
intent?
enum
"primary"
size?
enum
"md"
gradient?
enum
"purple-blue"
visuals?
enum
"none"
shape?
enum
"circle"
direction?
enum
"ltr"
aria-label?
string
"Timeline"
className?
string
-
progressClassName?
string
-

Timeline.Header

PropTypeDefault
children
React.ReactNode
-
orientation?
enum
-
direction?
enum
-
className?
string
-

Timeline.Item

PropTypeDefault
title
string
-
children
React.ReactNode
-
orientation?
enum
-
intent?
enum
-
size?
enum
-
direction?
enum
-
visuals?
enum
-
shape?
enum
-
className?
string
-
contentClassName?
string
-
pinClassName?
string
-
aria-current?
enum
-

Accessibility

The Timeline component includes the following accessibility features:

  • Semantic HTML with <section>, role="list", and role="listitem"
  • Proper heading hierarchy with aria-labelledby relationships
  • Progress indicators are decorative and hidden from screen readers
  • Scroll-driven animations respect prefers-reduced-motion

Credits

  • Inspired by horizontal scroll timelines and scroll-driven animation patterns.

Built by raouf.codes. The source code is available on GitHub.

Last updated: 5/2/2026

On this page