WebberUI

Scroll Timeline

A scroll-driven timeline where the growing line extends along the main axis and each node lights up and reveals its content as the line arrives.

Loading preview…
npx shadcn@latest add https://webberui.com/r/scroll-timeline.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

0.5
<ScrollTimeline />

Installation

npx shadcn@latest add https://webberui.com/r/scroll-timeline.json

Or, once registries are configured in components.json, install it as @webberui/scroll-timeline.

Usage

Wrap a set of ScrollTimelineItem elements in ScrollTimeline. As you scroll, the growing line on the central axis extends from top to bottom and each node lights up as the line reaches it.

import { Flag, Rocket } from "lucide-react";
import {
  ScrollTimeline,
  ScrollTimelineItem,
} from "@/components/ui/scroll-timeline";

<ScrollTimeline>
  <ScrollTimelineItem icon={<Flag />} date="2024 · Q1" title="Project kickoff">
    Settled on an animation-first design principle.
  </ScrollTimelineItem>
  <ScrollTimelineItem icon={<Rocket />} date="2025 · Q1" title="Public launch">
    Scroll to here and the node lights up together with the growing line.
  </ScrollTimelineItem>
</ScrollTimeline>;

If the timeline sits inside a fixed-height nested scroll container, pass that container's ref to container:

const scrollerRef = React.useRef<HTMLDivElement>(null);

<div ref={scrollerRef} className="h-[300px] overflow-y-auto">
  <ScrollTimeline container={scrollerRef}>{/* ... */}</ScrollTimeline>
</div>;

Props

ScrollTimeline

PropTypeDefaultDescription
childrenReact.ReactNodeA set of ScrollTimelineItem elements
containerReact.RefObject<HTMLElement | null>Ref of the nested overflow scroll container; when omitted, the window is the scroll container
activationLinenumber0.5Position of the trigger line within the scroll viewport (0 = top, 1 = bottom); both the tip of the growing line and node activation use it as their reference
classNamestringAppended to the class of the outermost container

ScrollTimelineItem

PropTypeDefaultDescription
titleReact.ReactNodeNode title
dateReact.ReactNodeDate or stage label, shown above the title
iconReact.ReactNodeNode icon (a lucide-react icon is recommended), shown inside the node once it lights up
childrenReact.ReactNodeBody text describing the node
classNamestringAppended to the class of the node's li

How it works

  • The tip of the growing line and the moment each node lights up both use the same "trigger line" (activationLine) as their reference, so no matter how much the nodes differ in height, a node always lights up the instant the growing line reaches it — the two stay precisely in sync.
  • The end of the main axis carries a "comet tail" glow that follows the leading edge of the growing line, reinforcing the sense of scroll direction.
  • Nodes and their content brighten smoothly from faded as the scroll progresses; nodes not yet reached stay as hollow shapes with a pale outline.

Accessibility

  • When the user has "reduce motion" enabled at the system level, the timeline is presented statically with the whole line lit and all content visible, and it does not change with the scroll.
  • The axis, growing line, nodes, and glow are decorative only, so they are marked aria-hidden; the timeline content is carried by a semantic ol / li structure, which assistive technology reads out as a complete list.
  • The nested scroll container example adds tabIndex and an aria-label so keyboard users can focus it and scroll with the arrow keys.

On this page