Join JS Mastery Pro to apply what you learned today through real-world builds, weekly challenges, and a community of developers working toward the same goal.
Creating a website that feels alive and interactive often comes down to the little details and one of the most eye-catching is the parallax scrolling effect. You’ve probably seen it on award-winning websites: as you scroll, the background glides by more slowly than the content in front, creating a sense of depth and motion. It’s almost like looking through layers of a moving scene rather than a flat screen.
In this tutorial, we’ll show you how to bring that effect to life using GSAP (GreenSock Animation Platform) and its powerful ScrollTrigger plugin. GSAP is a favorite among developers for its smooth performance and fine-grained control, making it the perfect tool to craft professional-grade animations that work seamlessly across devices.
Before we can animate anything, let’s install the core library and its React integration package:
npm install gsap @gsap/reactIf you’re working in React, you’ve probably used useEffect() to handle animations before. It works, but there’s a catch:
React’s strict rendering and batching can cause timing issues when animations rely on DOM measurements or element mounting.
That’s where @gsap/react comes in.
It gives us the useGSAP() hook, a purpose-built helper that ensures our GSAP animations run only after the component has fully mounted and with built-in cleanup.
So instead of:
useEffect(() => { gsap.to(...); }, []);We’ll use:
useGSAP(() => { gsap.to(...); });It’s cleaner, safer, and fully optimized for React environments.
GSAP itself is modular. Some features (like scroll effects, draggable, flip, etc.) live in separate plugins.
We’ll use two of them here:
To use them, we have to register them once before calling.
This tells GSAP to load their functionality into the core engine:
import gsap from 'gsap';
import { ScrollSmoother, ScrollTrigger } from 'gsap/all';
gsap.registerPlugin(ScrollSmoother, ScrollTrigger);Without registration, you’ll see “invalid property” errors, because GSAP doesn’t know about these plugins yet.
Before we initialize ScrollSmoother, we need a very specific DOM structure.
This plugin works by separating the “scroll container” from the “content that moves”.
<div id="smooth-wrapper">
<div id="smooth-content">
<!-- all scrollable elements go here -->
</div>
</div>Here’s why:
When you scroll, the smoother intercepts wheel/touch input and applies easing, creating that signature cinematic glide.
So make sure all your animated sections - including your .holder images — live inside #smooth-content
Now that the structure is ready, we can finally activate the plugin.
According to the official GSAP ScrollSmoother docs, the basic setup looks like this:
ScrollSmoother.create({
smooth: 2,
effects: true,
});Let’s bring that into our App.jsx:
import gsap from 'gsap';
import { ScrollSmoother, ScrollTrigger } from 'gsap/all';
gsap.registerPlugin(ScrollSmoother, ScrollTrigger);
const App = () => {
useGSAP(() => {
ScrollSmoother.create({
smooth: 2, // smoothness factor
effects: true, // enables data-speed parallax
});
});
return (
<section>
<div className="symbol-wrapper">
<img src="/images/symbol.svg" className="symbol" />
<h1>@cain.restaurante</h1>
<div className="diamond-wrapper">
<img src="/images/diamond.svg" />
<img src="/images/diamond.svg" />
<img src="/images/diamond.svg" />
<img src="/images/diamond.svg" />
</div>
</div>
{/* 👇 Important GSAP wrapper */}
<div id="smooth-wrapper">
<div id="smooth-content">
<div className="relative py-96 h-[250vh]">
<div className="absolute inset-0">
<div className="holder" data-speed="1.2">
<img src="/images/img1.png" />
</div>
<div className="holder" data-speed="1">
<img src="/images/img2.png" />
</div>
<div className="holder" data-speed="0.9">
<img src="/images/img3.png" />
</div>
<div className="holder" data-speed="0.8">
<img src="/images/img4.png" />
</div>
<div className="holder" data-speed="0.7">
<img src="/images/img5.png" />
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export default App;The data-speed attribute is how ScrollSmoother knows how fast or slow each element should move relative to the scroll position.
This small detail creates a cinematic depth illusion - exactly what gives modern parallax sites that floating, layered look.
That’s it.
You now have a fully working GSAP ScrollSmoother parallax system, built the right way for React.
If you wish to become a creative developer and specialize in web animations, consider diving into my Web Animation Course, where I cover modern animation, emotional design, and mastering GSAP.