August 8, 2025

Md. Saad

One of the most effective ways to make webpages come to life is with scroll-triggered animations. With Motion.dev (the updated version of Framer Motion), you can animate objects as users scroll, whether you're creating a landing page, product showcase, or narrative layout.
I'll go over three key methods in this tutorial for using Motion to make scroll-based and scroll-triggered animations:
Let’s start simple. Consider a scenario in which you want an element to fade out as it enters the viewport, such as a blue box that disappears to reveal content underneath. Making your div into a motion is the simplest way to accomplish this.div, establishing its starting opacity, and employing the whileInView prop to initiate a fresh opacity upon its scrolling into view. We'll also make sure it only runs once and adjust when it triggers.
import { motion } from "motion/react"
<motion.div
initial={{ opacity: 1 }}
whileInView={{ opacity: 0 }}
transition={{ duration: 1 }}
viewport={{ amount: 'all', once: true }}
className="h-64 w-64 bg-blue-500"
>
<p className="text-white text-xl">Content behind the blue box</p>
</motion.div>Use this when you want something to fade, scale, or move when it enters the screen.
Next, let's say you want to animate a group of boxes when a section enters the viewport. To accomplish this, set a reference to a wrapper element and use the useInView hook to determine when the container is visible on the screen. Once that has been identified, you can use the same Boolean state to start animations on any child elements.
import { useRef } from "react"
import { useInView, motion } from "motion/react"
const MultiAnimate = () => {
const ref = useRef(null)
const isInView = useInView(ref, { amount: 'all' })
return (
<div ref={ref} className="flex space-x-4 mt-20">
<motion.div animate={{ y: isInView ? -100 : 0 }} className="w-1/3 h-32 bg-red-500" />
<motion.div animate={{ y: isInView ? 100 : 0 }} className="w-1/3 h-32 bg-green-500" />
<motion.div animate={{ y: isInView ? -100 : 0 }} className="w-1/3 h-32 bg-blue-500" />
</div>
)
}Use this when you want to animate multiple items — cards, steps, icons — in sync.
More than just "in or out of view" is sometimes required; in some cases, you want to animate according to the user's scrolling distance. useScroll and useTransform are useful in this situation. The scroll progress (scrollYProgress) will be tracked, and the value (0 to 1) will be mapped into animations such as fading, scaling, and rotation.
3.1 Scroll-Based Progress Bar
To create a scroll indicator like a reading bar, use the scroll progress to scale a horizontal element.
const { scrollYProgress } = useScroll()
<motion.div
className="fixed top-0 left-0 h-1 bg-blue-600 origin-left"
style={{ scaleX: scrollYProgress }}
/>Perfect for blogs, articles, or long content.
3.2 Animate Background Color Based on Scroll
You can also transform scrollYProgress into a background color change.
const backgroundColor = useTransform(
scrollYProgress,
[0, 1],
["#ffffff", "#3b82f6"]
)
<motion.div
className="h-screen"
style={{ backgroundColor }}
/>Use this for section transitions or mood shifts on scroll.
3.3 Rotate Based on Element’s Scroll Visibility
To animate based on when a specific element is entering and exiting, set a target ref in useScroll and apply transformations to that element only.
const targetRef = useRef(null)
const { scrollYProgress } = useScroll({
target: targetRef,
offset: ["start end", "end start"]
})
const rotate = useTransform(scrollYProgress, [0, 1], [0, 180])
<motion.div
ref={targetRef}
style={{ rotate }}
className="h-64 w-64 bg-indigo-500 mx-auto"
></motion.div>Use this for rotating cards, spinning logos, or visual storytelling elements.
Use offset if you want exact control over when the animation occurs. Sayings like "start when the top hits the center" or "end when 25% has passed" are made possible by this.
const { scrollYProgress } = useScroll({
target: targetRef,
offset: ["start start", "end start"]
})You can replace start, end with center, 25%, or even 200px.
Sometimes your scroll container isn’t the page — it’s a horizontal slider or div. You can still use useScroll, but you’ll set both a container and a target to track progress.
const containerRef = useRef(null)
const targetRef = useRef(null)
const { scrollXProgress } = useScroll({
container: containerRef,
target: targetRef,
axis: "x",
offset: ["end start", "start start"]
})
<motion.div
ref={targetRef}
style={{ opacity: scrollXProgress }}
className="w-64 h-64 bg-purple-500"
></motion.div>Use this for image sliders, horizontal timelines, or scroll-triggered reveals.
Regardless of whether you're making a one-page website or a complex user interface, scroll animations increase user engagement and guide the user's eye. Motion.dev makes it easy to use, expressive, and highly customizable.
To make your website smooth and visually appealing, enhancing the user experience, contact us. We will guide you on the journey.