Scrollytelling can be complicated to implement on a website without the use of scroll driven animation libraries. These libraries provide a simple interface for creating different interactions on the scroll and improving user experience.
Over the years, there has been a significant improvement in scroll driven animation libraries that power scrollytelling in JavaScript. From the launch of SuperScrollorama by John Polacek in 2013 to ScrollMagic by Jan Paepke in 2014, and the recent announcement of ScrollTrigger by GSAP in 2020.
ScrollTrigger is the reinvention of scroll-driven animation but in a more suitable and user-friendly way, giving you the ability to control the flow of GSAP animations or any other thing while scrolling.
In this tutorial, we will learn about the GSAP ScrollTrigger plugin, how to use it to trigger animations on the scroll, and the use cases for it. In the process, we will build a landing page in React that uses GSAP for animations and ScrollTrigger to trigger the animations. By the end of this tutorial, you will understand the basics of the GSAP ScrollTrigger plugin and how to use it in React.
This tutorial assumes the reader has the following:
GSAP is an acronym for the GreenSock Animation Platform. It is arguably the best animation library for the web because it can animate DOM elements, canvas, SVG, CSS, WebGL, generic JavaScript objects, and so much more.
The creators of GSAP strongly believe it is the fastest full-featured scripted animation tool on the planet.
ScrollTrigger is built on the GSAP and can be used to trigger those intriguing GSAP animations on the scroll with just a few lines of code, excellent performance, cross-browser compatibility, and support from the GSAP community.
In this section we’ll look at the importance of ScrollTrigger, and when you should use it.
There are three circles in the demo below. The third circle has been animated with GSAP to move along the x-axis
of the page for two seconds. The third circle might not be visible to you at first glance, so you need to scroll down.
ScrollTrigger Demo 0
No Description
On scrolling down, you’ll notice that the third circle had already moved towards the x-axis
before you reached that section of the page. Yeah, I know. That’s not cool.
The good news is, ScrollTrigger solves this problem with its ability to let you trigger the animations when a user reaches a specified viewport while scrolling.
Here are some of the things you can do with ScrollTrigger:
Find out more about these possibilities on ScrollTrigger’s website.
Before we trigger our animations on the scroll using ScrollTrigger, let’s get familiar with the basics.
This tutorial assumes that the reader has a fair understanding of how to animate using GSAP, so we will only cover the basics of ScrollTrigger. Find a fantastic resource to get you up to speed with GSAP here.
The ScrollTrigger basics you will learn in this section will be used in building our project later in this tutorial. You can check out the full list of ScrollTrigger properties and methods in their documentation.
If you already know the fundamentals of ScrollTrigger, you can skip this section and jump straight to the project section, where we’ll build a simple landing page in React and trigger the animations on the scroll with ScrollTrigger.
The trigger
property is used to specify the point we want our animation to start. To add a trigger
property, use the syntax below:
trigger: "element"
With this property, we can make the third circle in our previous demo the trigger point for the animation. This means that the circle will only move towards the x-axis when we reach the specified element’s (the third circle, in this case) viewport.
Let’s set the #thirdCircle
as the trigger point and see if anything changes:
trigger: "#thirdCircle"
Moment of truth! 😱
ScrollTrigger Demo 1
No Description
Now, you can see that the #thirdCircle
starts animating as soon as it comes into view. Exciting right?
markers
is a development-enabling feature that allows you to see the start and end position of the element to be animated and the page’s viewport.
Let’s add markers
to our previous demo using the syntax below:
markers: true
ScrollTrigger Demo 2
No Description
If we scroll down to the trigger element (the third circle), we will notice that the start
and end
position is now visible thanks to markers
.
By default, the trigger element starts animating as soon as it enters the bottom of the scroller’s viewport.
However, we can change the default start
position using the ScrollTrigger start
property.
Let’s change the start
position of the animation in our previous demo using the code below:
start: "top center"
ScrollTrigger Demo 3
No Description
top
refers to the top of the trigger element, which is thirdCircle
in this casecenter
signifies the center of the webpageAside from using keywords like top
, bottom
, and center
to control the animation’s position, we can also use pixel or percentage values.
Just like we could change the start
position, we can also change the end
position using ScrollTrigger’s end
property.
By default, the trigger element stops animating as soon the bottom enters the top of the scroller’s viewport. Let’s change the end
position of the animation in our demo using the code below.
end: "bottom top"
ScrollTrigger Demo 4
No Description
bottom
signifies the bottom of the trigger element thirdCircle
top
here signifies the top of the webpageThe scrub
property links the scroll position to an animation’s progress. When you set scrub
to true
, it makes the scroll bar act as a scrubber while it’s between the start
and end
position property.
Let’s add a scrub using the syntax scrub:true
to our demo to get a clearer understanding:
ScrollTrigger Demo 5
No Description
If you scroll through the page and look closely, you will notice that the trigger element (thirdCircle
) moves along the x-axis when you scroll down, but if you scroll up again, it takes it back to the original position, thereby acting like a scrubber.
So far, we’ve learned that ScrollTrigger allows you to trigger an animation when the page is scrolled to a specific viewport. We’ve also covered the basics of ScrollTrigger, now let’s move on to using ScrollTrigger in a React app.
Yay! The time we’ve all been waiting for is here. In this section, we will build a basic landing page with React, animate the landing page, and trigger the animations using ScrollTrigger.
Check out the demo on Codesandbox and code repository on GitHub.
This section has been broken down into different steps to make it easier to understand.
First, let’s spin up a React application by using Create React App:
npx create-react-app scrolly
The command above will create a React application called scrolly
for us. Once the application has been created successfully, switch to its repository using cd scrolly
and run the command below:
npm start
If the React app has been created correctly, you should see this in your browser window when you navigate to localhost:3000
.
Open the newly created React app in your favorite integrated development environment (IDE) and paste the code below inside the App.js file:
import './App.css'; import workout from "./workout.svg"; import greensocklogo from "./greensocklogo.svg"; import happy from "./happy.svg"; function App() { return ( <div className="App"> <div className="first"> <h1>ScrollTrigger</h1> <p className="first-paragraph"> is the coolest Greensock plugin. <span role="img" aria-label="celebrating"> 🥳 </span> </p> <div className="logo-main"> <img src={workout} id="workout-logo" alt="workout" /> </div> </div> <div className="second"> <div className="logo-main"> <img src={greensocklogo} id="gsap-logo" alt="greensocklogo" /> </div> </div> <div className="third"> <p> <span className="line" /> </p> <div className="logo-main"> <img src={happy} id="happy-logo" alt="happy" /> </div> </div> </div> ); } export default App;
The code above is a basic functional React component that we will use to animate our landing page.
Now, let me explain the code bit by bit:
import './App.css'; import workout from "./workout.svg"; import greensocklogo from "./greensocklogo.svg"; import happy from "./happy.svg";
Here, we imported all the external files we need to build the landing page.
The SVG images imported in the code snippet above can be found here. To add these files to your React app, create a file for each of the SVG images (workout.svg, greensocklogo.svg, and happy.svg), copy the SVG code here, and paste it into the files you created:
function App() { return ( <div className="App"> <div className="first"> <h1>ScrollTrigger</h1> <p className="first-paragraph"> is the coolest Greensock plugin. <span role="img" aria-label="celebrating"> 🥳 </span> </p> <div className="logo-main"> <img src={workout} id="workout-logo" alt="workout" /> </div> </div> <div className="second"> <div className="logo-main"> <img src={greensocklogo} id="gsap-logo" alt="greensocklogo" /> </div> </div> <div className="third"> <p> <span className="line" /> </p> <div className="logo-main"> <img src={happy} id="happy-logo" alt="happy" /> </div> </div> </div> ); } export default App;
Here, we have a parent div
element with three child div
elements. This section controls what is displayed on our landing page. Finally, we exported our App
component so that we can reuse it in different sections of the React app.
If you added the SVG’s images correctly, there shouldn’t be any error, and the landing page should look like this now.
As you can see, our landing page isn’t styled adequately, so make it more appealing with CSS. To do this, paste the code below into your App.css
file:
*{ padding: 0%; margin: 0%; box-sizing: border-box; } .first{ width: auto; height:500px; background-color: #fdfffc; margin-top: 30px; } .second{ width:auto; height: 400px; background-color: #e0fbfc; } .third{ width: auto; height: 400px; background-color: #fdfffc; } .line { width: 100%; max-width: 1400px; height: 20px; margin-top:20px; position: relative; display: inline-block; background-color:#023047; } h1{ text-align: center; padding-top: 90px; color: #023047; font-size: 60px; } .first-paragraph{ text-align: center; color: #023047; font-size: 20px; font-weight: bold; } .logo-main{ align-items: center; display: flex; justify-content: center; } #workout-logo{ width: 500px; height: 300px; margin-top: 10px; } #gsap-logo{ width: 500px; height: 300px; margin-top: 50px; padding-right: 80px; } #happy-logo{ width: 500px; height: 300px; } .second-paragraph{ font-size: 30px; margin-top: 40px; } .third-paragraph{ font-size: 30px; margin-top: 40px; }
With the CSS we just added, our app should look like this.
We need to target the elements we intend to animate on the landing page using the useRef()
React Hook.
Paste the updated code below into your App.js
file:
import {useRef} from "react"; import "./App.css"; import workout from "./workout.svg"; import greensocklogo from "./greensocklogo.svg"; import happy from "./happy.svg"; function App() { const ref = useRef(null); return ( <div className="App" ref={ref}> <div className="first"> <h1>ScrollTrigger</h1> <p className="first-paragraph"> is the coolest Greensock plugin. <span role="img" aria-label="celebrating"> 🥳 </span> </p> <div className="logo-main"> <img src={workout} id="workout-logo" alt="workout" /> </div> </div> <div className="second"> <div className="logo-main"> <img src={greensocklogo} id="gsap-logo" alt="greensocklogo" /> </div> </div> <div className="third"> <p> <span className="line" /> </p> <div className="logo-main"> <img src={happy} id="happy-logo" alt="happy" /> </div> </div> </div> ); } export default App;
Here’s an explanation of the code changes:
import {useRef} from "react";
We imported the useRef()
React Hook into App.js
:
const ref = useRef(null);
Then we created a variable called ref
that will be used to store the reference of our target element (the parent div
element in this case).
<div ref={ref} className="App"> ... </div>
Finally, we passed the mutable ref
object into the parent div
element.
Now that we have targeted the element we intend to animate, let’s animate it with GSAP.
First, install GSAP into your React app by running the command below in your terminal:
npm install gsap
After installing GSAP into the React app successfully, paste the updated code below into your App.js
file:
import { useRef, useEffect } from "react"; import "./App.css"; import workout from "./workout.svg"; import greensocklogo from "./greensocklogo.svg"; import happy from "./happy.svg"; import { gsap } from "gsap"; function App() { const ref = useRef(null); useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector(".first-paragraph"), { opacity: 0, y: -20 }, { opacity:1, y: 0 } ); }, []); useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector("#gsap-logo"), { opacity: 0, scale: 0.2, y: -20 }, { opacity: 1, y: 0, scale: 1, duration: 1, ease: "none" } ); }, []); useEffect(() => { const element = ref.current; gsap.from(element.querySelector(".line"), { scale: 0, ease: "none" }); }, []); return ( <div className="App" ref={ref}> <div className="first"> <h1>ScrollTrigger</h1> <p className="first-paragraph"> is the coolest Greensock plugin. <span role="img" aria-label="celebrating"> 🥳 </span> </p> <div className="logo-main"> <img src={workout} id="workout-logo" alt="workout" /> </div> </div> <div className="second"> <div className="logo-main"> <img src={greensocklogo} id="gsap-logo" alt="greensocklogo" /> </div> </div> <div className="third"> <p> <span className="line" /> </p> <div className="logo-main"> <img src={happy} id="happy-logo" alt="happy" /> </div> </div> </div> ); } export default App;
Here’s an explanation of the changes we made to the existing code in our App.js file:
import { useRef, useEffect } from "react"; import { gsap } from "gsap";
We imported the useEffect()
React Hook and gsap
into App.js
:
useEffect(() => { const element = ref.current; ... }, []);
We initialized three different useEffect()
React Hooks for the three child div
elements. Inside each useEffect()
Hook, we created a constant variable called element
and assigned it a reference to the parent div
element we targeted using the useRef()
React Hook:
useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector(".first-paragraph"), { opacity: 0, y: -20 }, { opacity:1, y: 0 } ); }, []); useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector("#gsap-logo"), { opacity: 0, scale: 0.2, y: -20 }, { opacity: 1, y: 0, scale: 1, duration: 1, ease: "none" } ); }, []); useEffect(() => { const element = ref.current; gsap.from(element.querySelector(".line"), { scale: 0, ease: "none" }); }, []);
In each of the three hooks, we added GSAP tweens
to animate the three child div
elements; .first-paragraph
, #gsap-logo
, and .line
.
With this new update, our landing page now looks like this.
If you look at the landing page now, you will notice that we’ve successfully animated the .first-paragraph
, #gsap-logo
and .line
child elements.
However, these animations start animating once the page is loaded into the browser window preventing us from seeing our animations in action. I bet you didn’t see the animation of the .line
element at the end of the landing page.
Let’s fix this by adding the ScrollTrigger plugin to ensure the animations are only triggered on the scroll.
Yay! It is time to see the ScrollTrigger plugin in action.
Paste the code below into the App().js
file:
import { useRef, useEffect } from "react"; import "./App.css"; import workout from "./workout.svg"; import greensocklogo from "./greensocklogo.svg"; import happy from "./happy.svg"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; function App() { gsap.registerPlugin(ScrollTrigger); const ref = useRef(null); useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector(".first-paragraph"), { opacity: 0, y: -20 }, { opacity: 1, y: 0, scrollTrigger: { trigger: element.querySelector(".first"), start: "top top", end: "bottom center", scrub: true } } ); }, []); useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector("#gsap-logo"), { opacity: 0, scale: 0.2, y: -20 }, { opacity: 1, y: 0, scale: 1, duration: 1, ease: "none", scrollTrigger: { trigger: element.querySelector(".first"), start: "top center", end: "bottom top", scrub: true } } ); }, []); useEffect(() => { const element = ref.current; gsap.from(element.querySelector(".line"), { scale: 0, ease: "none", scrollTrigger: { trigger: element.querySelector(".third"), scrub: true, start: "top bottom", end: "top top" } }); }, []); return ( <div className="App" ref={ref}> <div className="first"> <h1>ScrollTrigger</h1> <p className="first-paragraph"> is the coolest Greensock plugin. <span role="img" aria-label="celebrating"> 🥳 </span> </p> <div className="logo-main"> <img src={workout} id="workout-logo" alt="workout" /> </div> </div> <div className="second"> <div className="logo-main"> <img src={greensocklogo} id="gsap-logo" alt="greensocklogo" /> </div> </div> <div className="third"> <p> <span className="line" /> </p> <div className="logo-main"> <img src={happy} id="happy-logo" alt="happy" /> </div> </div> </div> ); } export default App;
Here’s an explanation of the changes we made to the code:
import { ScrollTrigger } from "gsap/ScrollTrigger";
We imported the ScrollTrigger plugin into App.js
:
gsap.registerPlugin(ScrollTrigger);
Then we registered the ScollTrigger plugin:
useEffect(() => { const element = ref.current; gsap.fromTo( element.querySelector(".first-paragraph"), { opacity: 0, y: -20 }, { opacity: 1, y: 0 scrollTrigger: { trigger: element.querySelector(".first"), start: "top top", end: "bottom center", scrub: true } } ); }, []);
In each of our useEffect()
Hooks, we added scrollTrigger
as one of the values in our gsap tween
.
scrollTrigger: { trigger: element.querySelector(".first"), start: "top top", end: "bottom center", scrub: true }
We added our trigger element
in this case, the .first
element. Then we also specified the start
and end
position for the animation. Finally, we set scrub
to true
so that all our animations will act as a scrubber when we scroll through the landing page.
With the updated changes, our landing page’s animations will only start animating when we reach the specified viewport while scrolling through the web page.
See demo here.
I hope you were able to learn about the GSAP ScrollTrigger plugin in this tutorial. Even though this tutorial covers the fundamental possibilities of using ScrollTrigger, there’s so much more you can do with ScrollTrigger. You might be inspired by the demos on GreenSock’s CodePen.
If you have any questions, you can leave it in the comments section below.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
One Reply to "How to use the GSAP ScrollTrigger plugin in React"
Edidiong, girl, you saved my ass, thanks a ton!