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