# How to use the GSAP ScrollTrigger plugin in React

[Scrollytelling](https://purplepublish.com/en/scrollytelling/#:~:text=The%20word%20%E2%80%9CScrollytelling%E2%80%9D%20itself%20is,change%20according%20to%20the%20context) 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](http://johnpolacek.github.io/scrollorama/) by [John Polacek](https://twitter.com/johnpolacek?lang=en) in 2013 to [ScrollMagic](https://scrollmagic.io/) by [Jan Paepke](https://twitter.com/janpaepke) in 2014, and the recent announcement of [ScrollTrigger](https://greensock.com/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, you will learn about the GSAP ScrollTrigger plugin, how to use it to trigger animations on the scroll in React, and the use cases for it.

# Prerequisites

This tutorial assumes the reader has the following:

1. [Node](https://nodejs.org/en/) >= 8.10 installed on their local development machine
2. [npx](https://www.npmjs.com/package/npx) 5.2 or higher installed on their local development machine
3. Basic understanding of how to animate elements with GSAP
4. A basic knowledge of HTML, CSS, JavaScript, and React


# Introducing the GSAP ScrollTrigger plugin

GSAP is an acronym for the GreenSock Animation Platform. It is [arguably the best animation library for the web](https://greensock.com/why-gsap) because it can animate DOM elements, canvas, SVG, CSS, WebGL, generic JavaScript objects, and so much more.

[The creators of GSAP](https://greensock.com/why-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.


# Use case for ScrollTrigger

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.


%[https://codepen.io/edyasikpo/pen/RwRdWeE]

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.

# The possibilities of the GSAP ScrollTrigger plugin

Here are some of the things you can do with ScrollTrigger:

- Animate anything (DOM, CSS, SVG, WebGL, and Canvas) on the scroll
- Toggle playback state or scrub through animations
- Automatic resizing on different screens
- Support for vertical and horizontal scrolling
- Ability to pin elements in place

Find out more about these possibilities on [ScrollTrigger's website](https://greensock.com/scrolltrigger/).


# ScrollTrigger basics

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](https://ihatetomatoes.net/get-greensock-101).

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](https://greensock.com/docs/v3/Plugins/ScrollTrigger).

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.


## trigger

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! 😱

%[https://codepen.io/edyasikpo/pen/xxOBZpm]

Now, you can see that the `#thirdCircle` starts animating as soon as it comes into view. Exciting right?

## markers

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


%[https://codepen.io/edyasikpo/pen/zYBbrRJ]

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


## start

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"`

%[https://codepen.io/edyasikpo/pen/mdEoVLJ]

- Here, `top` refers to the top of the trigger element, which is `thirdCircle` in this case
- `center` signifies the center of the webpage

Aside from using keywords like `top`, `bottom`, and `center` to control the animation's position, we can also use pixel or percentage values.


## end

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"`

%[https://codepen.io/edyasikpo/pen/gOMEPKr]

- Here `bottom` signifies the bottom of the trigger element `thirdCircle`
- `top` here signifies the top of the webpage

## scrub

The `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:

%[https://codepen.io/edyasikpo/pen/ZEOPQMR]

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.


# 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](https://eqkvg.csb.app/) and code repository on [GitHub](https://github.com/didicodes/scrolly).

This section has been broken down into different steps to make it easier to understand.


## Step 1: Create a React project

First, let's spin up a React application by using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html):

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

![React Homepage](https://i.imgur.com/nX66vKg.png)


## Step 2: Add content to our React app

Open the newly created React app in your favorite integrated development environment (IDE) and paste the code below inside the App.js file:

``` javascript
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:

```javascript
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](https://codesandbox.io/s/scrolltrigger-example-06rqh?file=/src/App.js:282-435). 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](https://codesandbox.io/s/scrolltrigger-example-06rqh?file=/src/App.js:282-435), and paste it into the files you created:

```javascript
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. 👇🏽

%[https://codesandbox.io/s/n0uft]


## Step 3: Customize the landing page

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:

``` CSS
* {
	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. 👇🏽

%[https://codesandbox.io/s/06rqh]

## Step 4: Target elements using refs

We need to target the elements we intend to animate on the landing page using the [useRef](https://medium.com/trabe/react-useref-hook-b6c9d39e2022) React hook. 

Paste the updated code below into your  `App.js` file:

``` javascript
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:

```javascript
import {useRef} from "react";
```

We imported the `useRef()` React Hook into `App.js`:

```javascript
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).

```javascript
<div ref={ref} className="App">
    ...
</div>
```

Finally, we passed the mutable `ref` object into the parent `div` element. 


## Step 5: Animate the React ref using GSAP

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:

``` javascript
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: 

```javascript
import { useRef, useEffect } from "react";
import { gsap } from "gsap";
```

We imported the `useEffect()` React Hook and `gsap` into `App.js`:

```javascript
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:

```javascript
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. 👇🏽

%[https://codesandbox.io/s/yrlm0] 

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.


## Step 6: Trigger animations on the scroll using ScrollTrigger

Yay! It is time to see the ScrollTrigger plugin in action. 

Paste the code below into the `App().js` file:

```javascript
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:

```javascript
import { ScrollTrigger } from "gsap/ScrollTrigger";
```

We imported the ScrollTrigger plugin into `App.js`:

```javascript
gsap.registerPlugin(ScrollTrigger); 
```

Then we registered the ScollTrigger plugin:

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

```javascript
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 below. 👇🏽

%[https://codesandbox.io/s/scrolly-4-eqkvg?fontsize=14&hidenavigation=1&theme=dark]

---

# Conclusion

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 Code](https://codepen.io/GreenSock)[P](https://codepen.io/GreenSock)[en](https://codepen.io/GreenSock).

If you have any questions, you can leave them in the comments section below.

# Resources for further reading

- [GreenSock 101](https://ihatetomatoes.net/get-greensock-101/), Petr Tichy
- [GSAP Cheatsheet](https://greensock.com/cheatsheet/), GreenSock
- [ScrollTrigger Documentation](https://greensock.com/docs/v3/Plugins/ScrollTrigger), GreenSock
- [Most Common ScrollTrigger Mistakes](https://greensock.com/st-mistakes/), GreenSock


