GSAP + React, First Steps & Handy Techniques.

React is a hugely popular framework choice, and as evidenced by many of the sites in our showcase – React and GSAP can be a powerful combination. However, utilizing GSAP in React requires a different way of thinking than a vanilla JS project.

React is a hugely popular framework choice, and as evidenced by many of the sites in our showcase – React and GSAP can be a powerful combination. However, utilizing GSAP in React requires a different way of thinking than a vanilla JS project.

We’ve written this guide to help you get started using GSAP within a React project. This is not a tutorial, so feel free to dip in and out as you learn, think of it as a collection of recommended techniques and best practices to use in your projects.

 

Why GSAP?

Animating with GSAP gives you unprecedented levels of control and flexibility. You can reach for GSAP to animate everything — from simple DOM transitions to SVG, three.js, canvas or WebGL — your imagination is the limit. More importantly, you can rely on us. We obsess about performance, optimizations and browser compatibility so that you can focus on the fun stuff. We’ve actively maintained and refined our tools for over a decade and there are no plans to stop. Lastly, if you ever get stuck, our friendly forum community is there to help.

Going forward we will assume a basic understanding of GSAP and React.

If you’re just getting going with React, this tutorial from the React team is a great place to start.
Need a GSAP refresher? Take a break and read about tweens and timelines. We’ll be here when you get back.

 

 

Online PlaygroundsOnline Playgrounds

Get started quickly by forking one of these starter templates:

 

Create a new React AppCreate a new React App

If you prefer to work locally, Create React App provides a comfortable setup for experimenting with React and GSAP.

To create a project, run:

npx create-react-app gsap-app
cd gsap-app
npm start

Once the project is set up we can install GSAP through npm,

npm i gsap
npm start

then import it into our app.

    
import React from "react";
import { gsap } from "gsap";
 
export default function App() {
 return (
   <div className="app">
     <div className="box">Hello</div>
   </div>
 );
}

More detailed information about getting started with React

GSAP installation documentation

 

Targeting elementsTargeting elements

In order to animate using GSAP we need access to the element in the DOM. Refs provide a way for us to interact with and store references to DOM nodes in a React component.

const boxRef = useRef();

return <div className="box" ref={boxRef}>Hello</div>;

read more about refs in the React docs

 

Creating our first animationCreating our first animation

GSAP updates inline style properties, so it’s important to make sure the DOM has been rendered before trying to animate anything.

If we ask GSAP to animate an element that hasn’t been rendered, we’ll get this warning in the console.

GSAP target not found.

In order to avoid targeting a null element, we can use the useEffect hook. This hook tells React that our component needs to do something after rendering.

function App() {
  // store a reference to the box div
  const boxRef = useRef();

  // wait until DOM has been rendered
  useEffect(() => {
    gsap.to(boxRef.current, { rotation: "+=360" });
  });
  
  // DOM to render
  return <div className="box" ref={boxRef}>Hello</div>;
}

In this example, React will first render the box element to the DOM, then GSAP will rotate the box 360deg.

 

Targeting descendant elementsTargeting descendant elements

gsap.utils.selector()

Creating a ref for each and every element we want to animate can add a lot of noise to our code. We can avoid this by making use of GSAP’s selector utility to easily select descendant elements.

const el = useRef();
const q = gsap.utils.selector(el);

useEffect(() => {
   // Target ALL descendants with the class of .box
  gsap.to(q(".box"), { x: 100 });
}, []);

 

Forwarding refs

gsap.utils.selector() will target all descendants in the component tree.

Within a component based system, you may need more granular control over the elements you’re targeting. You can use ref forwarding to get access to specific nested elements.

 

Creating and controlling timelinesCreating a timeline

Up until now we’ve just used refs to store references to DOM elements, but they’re not just for elements.

Refs exists outside of the render loop – so they can be used to store any value that you would like to persist for the life of a component. If you’re coming from class based components, this should be familiar to you as it’s essentially the same as using ‘this’.

In order to avoid creating a new timeline on every render, it’s important to create the timeline inside an effect and store it in a ref.

function App() {
  const el = useRef();
  const q = gsap.utils.selector(el);
  const tl = useRef();
      
  useEffect(() => {            
    
    tl.current = gsap.timeline()
      .to(q(".box"), {
        rotate: 360
      })
      .to(q(".circle"), {
        x: 100
      });

  }, []);
  
  return (
    <div className="app" ref={el}>
      <Box>Box</Box>
      <Circle>Circle</Circle>
    </div>
  );
}

This will also allow us to access the timeline in a different effect and toggle the timeline direction.

 

Controlling when React runs our animation.controlling when react runs our animation

By default useEffect runs both after the first render and after every update. So every time our component’s state changes, it will cause a re-render, which will run our effect again.

We can control when useEffect should run by passing in an array of dependencies. To only run once after the first render, we pass in an empty array.

// only runs after first render
useEffect(() => {
  gsap.to(q(".box-1"), { rotation: "+=360" });
}, []);

// runs after first render and every time `someProp` changes
useEffect(() => {
  gsap.to(q(".box-2"), { rotation: "+=360" });
}, [someProp]);

// runs after every render
useEffect(() => {
  gsap.to(q(".box-3"), { rotation: "+=360" });
});

 

Reacting to changes in statereacting to changes in state

Now that we know how to control when an effect fires, we can use this pattern to react to changes in our component. This is especially useful when passing down props.

function Box({ children, endX}) {
  const boxRef = useRef();

  // run when `endX` changes
  useEffect(() => {
    gsap.to(boxRef.current, {
      x: endX
    });
  }, [endX]);
  
  return (
    <div className="box" ref={boxRef}>{children}</div>
  );
}

 

Animating on interactionAnimating on interaction

Interaction is one of the most exciting things about animating on the web! In order to hook into user interactions like hover, we can use callbacks.

const onEnter = ({ currentTarget }) => {
  gsap.to(currentTarget, { backgroundColor: "#e77614" });
};

const onLeave = ({ currentTarget }) => {
  gsap.to(currentTarget, { backgroundColor: "#28a92b" });
};

return (
  <div className="box" onMouseEnter={onEnter} onMouseLeave={onLeave}>
    Hover Me
  </div>
);

 

Avoiding flash of unstyled content (FOUC)avoiding fouc

As useEffect fires after the DOM has been painted, when fading in elements you may notice an undesired flash of unstyled content.

longerfouc.gif

In order to avoid the flash, we can replace useEffect with useLayoutEffect. useLayoutEffect functions exactly the same as useEffect, but React doesn’t run it until the DOM has been painted.

useLayoutEffect is especially useful when you need to make DOM measurements, so we highly recommend it when using our ScrollTrigger and FLIP plugins.

More information about useEffect vs useLayoutEffect.

 

Cleaning UpCleaning up

It’s a good idea to return a cleanup function in your effects to kill off any running animations and anything else that could cause a memory leak, like an event listener.

This is particularly important if an animation runs for a really long time, makes use of ScrollTrigger, or changes the state in a component.

useEffect(() => {
  const animation1 = gsap.to(".box1", { rotation: "+=360" });
  
  const animation2 = gsap.to(".box2", {
    scrollTrigger: {
      ...
    }
  });

  const onMove = () => {
    ...
  };
  window.addEventListener("pointermove", onMove);
    
  // cleanup function will be called when component is removed
  return () => {
    animation1.kill();
    animation2.scrollTrigger.kill();
    window.removeEventListener("pointermove", onMove);
  };
}, []);

 

We hope this article was helpful – If you have any feedback please leave us a comment below so we can smooth out the learning curve for future animators!

Feeling confident and want to learn more? Check out our follow up article – Advanced GSAP animations in React.

 


Print Share Comment Cite Upload Translate
APA
Blog | Sciencx (2024-03-29T14:41:53+00:00) » GSAP + React, First Steps & Handy Techniques.. Retrieved from https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/.
MLA
" » GSAP + React, First Steps & Handy Techniques.." Blog | Sciencx - Thursday July 22, 2021, https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/
HARVARD
Blog | Sciencx Thursday July 22, 2021 » GSAP + React, First Steps & Handy Techniques.., viewed 2024-03-29T14:41:53+00:00,<https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/>
VANCOUVER
Blog | Sciencx - » GSAP + React, First Steps & Handy Techniques.. [Internet]. [Accessed 2024-03-29T14:41:53+00:00]. Available from: https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/
CHICAGO
" » GSAP + React, First Steps & Handy Techniques.." Blog | Sciencx - Accessed 2024-03-29T14:41:53+00:00. https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/
IEEE
" » GSAP + React, First Steps & Handy Techniques.." Blog | Sciencx [Online]. Available: https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/. [Accessed: 2024-03-29T14:41:53+00:00]
rf:citation
» GSAP + React, First Steps & Handy Techniques. | Blog | Sciencx | https://www.scien.cx/2021/07/22/gsap-react-first-steps-handy-techniques/ | 2024-03-29T14:41:53+00:00
https://github.com/addpipe/simple-recorderjs-demo