Scroll reveal
Also called reveal on scroll, animate on scroll, or scroll-triggered animation
An animation that fades or slides content into place as it scrolls into view, so each section appears just as the reader reaches it.
import { motion, useReducedMotion } from 'motion/react';
const features = [
{
title: 'Describe it',
text: 'Tell the agent what to build in plain words.'
},
{ title: 'Preview it', text: 'Watch the app update live as it builds.' },
{ title: 'Publish it', text: 'Put it online with one click.' },
{ title: 'Store data', text: 'Every app comes with a database.' },
{ title: 'Send email', text: 'Send receipts and sign-in links.' },
{ title: 'Track usage', text: 'See visits and costs per project.' }
];
const COLUMNS = 3;
const ROW_STAGGER = 0.08; // seconds between cards in the same row
export function FeatureSection() {
const reduceMotion = useReducedMotion();
return (
<section className='grid gap-4 sm:grid-cols-3'>
{features.map((feature, index) => (
<motion.article
key={feature.title}
initial={reduceMotion ? false : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
// Play once, as soon as 30% of the card is on screen.
viewport={{ once: true, amount: 0.3 }}
transition={{
duration: 0.5,
ease: [0.23, 1, 0.32, 1],
delay: (index % COLUMNS) * ROW_STAGGER
}}
className='rounded-2xl border bg-background p-6'
>
<h3 className='font-semibold'>{feature.title}</h3>
<p className='mt-1 text-sm text-muted-foreground'>{feature.text}</p>
</motion.article>
))}
</section>
);
}Reveal each feature card on scroll: when 30% of a card enters the viewport, fade it in and slide it up 24px over 500ms with an ease-out curve, cubic-bezier(0.23, 1, 0.32, 1). Animate each card only the first time it appears, stagger cards in the same row by 80ms, and skip the animation when prefers-reduced-motion is set.
Paste into Layerwise or any AI coding tool.
What is Scroll reveal?
A scroll reveal waits until an element enters the viewport, then plays a short entrance: a fade, a slide up of 20 to 40px, or a gentle scale. Sections seem to arrive right as the reader gets to them, which gives a long landing page some rhythm.
Scrolling only starts the animation. Once the element crosses a threshold, the entrance plays at its own speed, even if the reader stops scrolling. Browsers detect the moment with the IntersectionObserver API, and libraries wrap it for you; in Motion it is the whileInView prop.
Keep reveals quick (300 to 600ms) and subtle, and trigger them before the element is fully visible so nobody stares at blank space. Reveal each element once, keep important content readable even if the script fails, and show everything immediately for people who prefer reduced motion.
Often confused with
- Scroll-driven animation
- A scroll-driven animation is tied to the scroll position and rewinds when you scroll back up. A scroll reveal plays once, at its own speed, when the element appears.
- Lazy loading
- Lazy loading waits to download images or code until they are about to be seen. A scroll reveal animates content that is already on the page.
Related terms
All termsFade in
An entrance animation where an element goes from fully transparent to fully visible, so it appears gently instead of popping in.
Stagger
Animating a group of items one after another with a small delay between each, so they arrive as a cascade.
Reveal animation
An entrance where a moving edge uncovers an element, as if a curtain slides away, usually built with an animated clip-path.
Scroll-driven animation
An animation whose progress is tied directly to the scroll position, so it moves forward as you scroll down and rewinds as you scroll up.
Build it with Layerwise
Describe your app, watch it run in a live preview, and publish it with a database, storage, and sign-in built in.