Vercel Snap Text

A scroll-driven text list that snaps exactly to each item. The active item scales up with an optional prefix, others fade and indent — all animated with Motion springs.

Installation

Pro components require registry authentication. Add your Unlumen UI Pro key as UNLUMEN_LICENSE_KEY in your .env.local file and follow the setup guide.

File Structure

vercel-snap-text.tsx

Usage

import { VercelSnapText } from "@/components/unlumen-ui/vercel-snap-text";

<VercelSnapText
  items={["the platform", "the brand", "the future"]}
  prefix="We Design"
  className="w-full h-screen"
/>;

How it works

The component renders two layers inside a single container:

  1. Scroll driver — an invisible overflow-y: scroll overlay with scroll-snap-type: y mandatory. Each item gets a full-height snap section so the browser always stops exactly on an item boundary. No JavaScript rounding required.

  2. Visual layer — a spring-animated track that moves to center the active item. Each row animates its horizontal offset (Motion spring), font size, and opacity based on its distance from the current scroll position.

API Reference

VercelSnapText

items
string[]

List of strings to cycle through.

prefix?
string
""

Text prepended before the active item label (e.g. "We Design").

itemHeight?
number
96

Height of each item row in pixels. All rows share this height — it is the snap unit.

stiffness?
number
200

Spring stiffness for the track animation. Higher = snappier.

damping?
number
28

Spring damping for the track animation. Higher = less bounce.

className?
string

Additional CSS classes. Set a height (e.g. h-screen) on the root.

Credits

Inspired by the scroll-snap text effect seen on vercel.com. Thanks to the Vercel design team for the original idea.

Notes

  • The component requires a fixed height on the root (e.g. h-screen, h-[600px]). Without it, the scroll driver has no height and snapping won't work.
  • The invisible scroll overlay uses scroll-snap-type: y mandatory with each item as a full-height snap section. The browser handles snapping natively — no JS rounding needed.
  • The track animation uses a Motion spring (stiffness: 280, damping: 30, mass: 0.8 by default). Increase stiffness for snappier transitions, increase damping to reduce bounce.
  • itemHeight defines the height of each row in the visual layer. All items share this height — it's the snap unit. Adjust it based on your font size.
  • Non-active items fade out proportionally to their distance from the current index (opacity = 1 - dist × 0.82, minimum 0.15).
  • The prefix text is always visible and doesn't animate. Only the item list scrolls.
  • Font size uses clamp(1.75rem, 4vw, 4.5rem) — it scales responsively. Override it via className if you need a different size.
  • Scrollbar is hidden via scrollbarWidth: "none" and -ms-overflow-style: none. The scroll area is fully invisible.
  • A ResizeObserver keeps sectionHeight in sync with the container — no stale measurements after window resize.

Keep in mind

Most components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.