Skip to main content
Motion & React4 min readNovember 2026

How do I animate an element when it scrolls into view with Framer Motion?

Short answer

Use the whileInView prop with initial: <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, amount: 0.3 }} />. For custom logic use the useInView hook, and for animation tied to scroll position use useScroll with useTransform.

Core Takeaway

  • whileInView animates when the element enters the viewport
  • viewport.once runs it only the first time
  • viewport.amount sets how much must be visible (0 to 1)
  • useInView returns a boolean for your own logic
  • useScroll and useTransform link motion to scroll position

Reveal on scroll

import { motion } from 'motion/react';

<motion.section
  initial={{ opacity: 0, y: 24 }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true, amount: 0.3 }}
  transition={{ duration: 0.4 }}
/>

Stagger children

const list = { visible: { transition: { staggerChildren: 0.08 } } };
const item = { hidden: { opacity: 0, y: 12 }, visible: { opacity: 1, y: 0 } };

<motion.ul variants={list} initial="hidden" whileInView="visible" viewport={{ once: true }}>
  {items.map(i => <motion.li key={i.id} variants={item} />)}
</motion.ul>

useInView for custom logic

const ref = useRef(null);
const isInView = useInView(ref, { once: true });
useEffect(() => { if (isInView) startCounter(); }, [isInView]);

Scroll-linked animation

For a progress bar or parallax that follows the scroll position, use useScroll to get scrollYProgress and useTransform to map it to a style. The browser also has native scroll-driven animations in CSS, which can run off the main thread where supported.

Keep scroll reveals subtle and short. Content that waits for an animation before it is readable frustrates users, and people with prefers-reduced-motion should see it immediately.

Frequently asked questions

Why does whileInView fire again when I scroll back?

Set viewport={{ once: true }} so it only runs the first time.

How do I trigger the animation earlier?

Use viewport.margin, for example margin: "0px 0px -100px 0px", or lower amount.

Does whileInView hurt performance?

It uses IntersectionObserver, which is cheap. Animate transform and opacity, not layout properties.

Can I do this without a library?

Yes. IntersectionObserver plus a CSS class works, and CSS scroll-driven animations cover many cases.

Related Guides & Examples

Scroll as an input

ExodeUI animations can take scroll progress as an input and keep the timing in the file.

Open the examples
ExodeUI

EXODEUI DESKTOP

A little space for good ideas.

Choose your platform to get the official ExodeUI installer.

Looking for Intel Mac or other builds? Explore the web studio or check all download options.

Prefer the browser? Try for free in your browser →