Skip to content
Template Empire
Browse components

Vertical Timeline

A free animated vertical timeline component for React and Next.js.

Our story

Six years of shipping, one milestone at a time

How Northbound went from three engineers and a prototype to a deployment platform 40,000 teams rely on every day.

  1. 2019

    Company founded

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

    12Design partners

  2. 2020

    Seed round closed

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

    $4.2MSeed round

  3. 2021

    Public launch

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

    10,400Teams in Q1

  4. 2023

    Series A and global expansion

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

    $28MSeries A

  5. 2025

    Platform and public API

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

    1,900+Integrations built

Vertical Timeline is a free React timeline component for Next.js, built with Motion and styled with Tailwind. It renders your chronology as a semantic ordered list under its own section heading, then, as the section scrolls into view, fills a spine between the date rail and the entries while each entry and its node light up once in sequence.

You pass a heading and an array of items, and the component owns both the composition and the choreography. Each row places the date in a right-aligned left rail, the entry title and body in the centre, and an optional supporting figure in a fixed right rail, so equivalent parts line up across every entry however long the copy runs. The spine fill is a single scrubbed transform, and every entry reveals itself once at a fixed scroll position with its node scaling in and lighting from idle to accent. The full list is authored into the page as real ordered-list markup, so crawlers, keyboard users and reduced-motion visitors always read the complete chronology in order, with the spine shown fully filled and every node lit.

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-vertical

Usage

import { TimelineVertical } from "@/components/timeline-vertical";

export default function Page() {
  return (
    <TimelineVertical
      eyebrow="Since 2019"
      heading="Six years of shipping, one milestone at a time"
      lead="How the company went from three engineers and a prototype to a platform 40,000 teams rely on."
      items={[
        { id: "founded", meta: "2019", title: "Company founded", description: "The first prototype ships.", metric: { value: "12", label: "Design partners" } },
        { id: "launch", meta: "2021", title: "Public launch", description: "The product opens to everyone.", metric: { value: "10,400", label: "Teams in Q1" } },
        { id: "series-a", meta: "2023", title: "Series A", description: "New funding opens three offices.", metric: { value: "$28M", label: "Series A" } },
      ]}
    />
  );
}

Props

PropTypeDefaultDescription
items{ id: string; title: string; description: string; meta?: string; metric?: { value: string; label: string } }[]requiredThe chronology, 3 to 8 entries, in display order. Each id is the stable React key and is used to detect a reorder; meta is an optional date or short label rendered in the left rail on the title's baseline, and metric is an optional supporting figure rendered in the right rail. The right rail is reserved as soon as any one entry supplies a figure, and then on every row so the figures align; a timeline with no figures at all gives the whole row to its copy.
eyebrowstringundefinedSmall label above the section heading, rendered in the accent text colour.
headingstringundefinedSection heading, rendered as the h2 that leads the section so the entry titles read as subordinate h3s.
leadstringundefinedStandfirst paragraph, rendered beside the heading from the lg breakpoint up and below it on smaller viewports.
classNamestringundefinedAdditional classes merged onto the section root.

Customisation

Vertical Timeline exposes four Tier 3 tokens, each defaulting to a semantic token so it themes with the rest of your site out of the box: timeline-line for the hairline spine track, timeline-fill for the accent fill that scrubs in, timeline-marker for a node the scroll has reached, and timeline-marker-idle for a node it has not. Override any of them from your own CSS without touching the component:

:root {
  --timeline-fill: var(--color-accent-400);
  --timeline-marker: var(--color-accent-400);
}

<TimelineVertical items={items} className="max-w-2xl" />

Frequently asked questions

Is Vertical Timeline free for commercial use?
Yes. Vertical Timeline is MIT licensed, so you can use it in personal and commercial projects without attribution.
Does the timeline stay accessible and fast?
Yes. The chronology is rendered as a real ordered list, so the full sequence is in the accessibility tree before any entry is reached, and the reveal never changes DOM order or visibility. The spine and nodes are decorative and hidden from assistive technology. Only transform and opacity are animated, inside space the list has already reserved, so there is no layout shift, and with reduced motion enabled nothing is animated or listens to scroll: every entry is visible, every node is lit and the spine is shown fully filled. Entries waiting below the fold are set to their starting state before the page first paints, so none is ever seen visible and then hidden, and the layout follows the section's own width rather than the viewport's.
How do I recolour the spine and markers?
Set the Tier 3 tokens from your own CSS. timeline-fill controls the accent fill that scrubs in as you scroll, timeline-line sets the hairline track behind it, timeline-marker sets the colour and halo of a node the scroll has reached, and timeline-marker-idle sets the colour of one it has not reached yet. Each one 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