Skip to content
Template Empire
Browse components

Horizontal Milestone Timeline

A free horizontal timeline component for React and Next.js with an accessible scrollable rail.

Company timeline

From prototype to platform

Seven years of shipping, from a rented studio to the public API teams build on.

  1. 2019

    Current

    Company founded

    Three engineers ship the first prototype from a rented studio and sign the earliest design partners.

  2. 2020

    Seed round closed

    A focused seed round funds the first full-time team and the move to a dedicated platform architecture.

  3. 2021

    Public launch

    The product opens to everyone, reaching ten thousand active teams in the first quarter after launch.

  4. 2023

    Series A and global expansion

    New funding opens offices on three continents and brings enterprise support to regulated industries.

  5. 2025

    Platform and public API

    An extensible platform and public API let customers build their own integrations on top of the core.

Customise

280px
0.45s
1.16
280px
22px

Code

<TimelineHorizontal
  milestones={[
    { id: "founded", date: "2019", title: "Company founded", description: "The first prototype ships." },
    { id: "launch", date: "2021", title: "Public launch", description: "The product opens to everyone." },
    { id: "series-a", date: "2023", title: "Series A", description: "New funding opens three offices." },
  ]}
/>

Timeline Horizontal is a free horizontal timeline React component for Next.js, built with Motion and styled with Tailwind. On wide screens it renders your milestones as a horizontally scrollable rail with a decorative progress spine, previous and next buttons, and scroll snap, and on narrow screens it renders the same ordered list as a normal vertical chronology with no overflow at all.

You pass an array of milestones and the component owns the rest: buttons, arrow keys and native wheel or touch scrolling all resolve to the same active milestone, tracked by a debounced IntersectionObserver so the progress fill and active marker settle once scrolling stops rather than flickering mid-scroll. Reduced motion keeps every control working, it just swaps smooth scrolling and animated transitions for instant ones, and the mobile layout has no scroll-driven motion to begin with.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/timeline-horizontal

Usage

import { TimelineHorizontal } from "@/components/timeline-horizontal";

export default function Page() {
  return (
    <TimelineHorizontal
      milestones={[
        { id: "founded", date: "2019", title: "Company founded", description: "The first prototype ships." },
        { id: "launch", date: "2021", title: "Public launch", description: "The product opens to everyone." },
        { id: "series-a", date: "2023", title: "Series A", description: "New funding opens three offices." },
      ]}
    />
  );
}

Props

PropTypeDefaultDescription
milestones{ id: string; date: string; title: string; description: string }[]requiredThree to eight ordered milestones with unique, non-empty ids. Invalid input renders nothing.
initialIdstringundefinedInitial active milestone id. Falls back to the first milestone when omitted or unmatched.
spacingnumber280Wide-screen milestone width in CSS pixels, clamped to 220 through 380.
fillDurationnumber0.45Progress-fill transition duration in seconds, clamped to 0 through 1.
markerScalenumber1.16Active marker scale multiplier, clamped to 1 through 1.35.
snap"mandatory" | "proximity" | "none""proximity"Horizontal CSS scroll-snap treatment for the wide-screen rail.
railHeightnumber280Wide-screen rail minimum height in CSS pixels, clamped to 220 through 420.
radiusnumber22Milestone card corner radius in CSS pixels, clamped to 0 through 42.
onMilestoneChange(id: string) => voidundefinedCalled with a milestone's id once a button, keyboard, or scroll activation settles on it.
labelstring"Milestones"Accessible name shared by the timeline region and its ordered list.
classNamestringundefinedAdditional classes merged onto the section root.

Customisation

Timeline Horizontal exposes four Tier 3 tokens, each defaulting to a semantic token so it themes with the rest of your site out of the box: horizontal-timeline-accent for the completed and active markers and the progress fill, horizontal-timeline-track for the muted rail and connecting line, horizontal-timeline-marker for the outline ring on upcoming markers, and horizontal-timeline-panel for the milestone card surface. Override any of them from your own CSS without touching the component:

:root {
  --horizontal-timeline-accent: var(--color-accent-400);
  --horizontal-timeline-panel: var(--color-neutral-900);
}

<TimelineHorizontal milestones={milestones} spacing={320} />

Frequently asked questions

Is Timeline Horizontal free for commercial use?
Yes. Timeline Horizontal is MIT licensed, so you can use it in personal and commercial projects without attribution.
Does the rail stay accessible and fast on every screen?
Yes. The milestones are one semantic ordered list at every width, so visual order, DOM order and chronological order always match. On wide screens the rail is keyboard-scrollable with a visible focus ring, ArrowLeft, ArrowRight, Home and End move between milestones, and the previous and next buttons are correctly disabled at the ends. On narrow screens the rail becomes a plain vertical list with no overflow, no scroll-snap and no observer. Only transform and opacity are animated, and under reduced motion the buttons, keyboard and observer keep working, they just apply their target state instantly instead of animating.
How do I recolour the rail or change its spacing?
Set the Tier 3 tokens from your own CSS to recolour the spine, markers or panel, and use the spacing, railHeight and radius props to reshape the wide-screen rail. Each token defaults to a semantic token, so the timeline matches your theme until you choose to override it.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates