Setting up GSAP Animations on NextJS 13

Setting up GSAP Animations on NextJS 13

Start creating beautiful web animations with GSAP and NextJS 13

I've used various React animation libraries such as Framer Motion, React Spring and others! However, GSAP (GreenSock Animation Platform) has proven itself to be my favorite JavaScript/React animation library repeatedly. Originally released in 2006, GSAP has amassed a reputation for being a powerful tool for creating unique web experiences.

Examples of websites I created using the power of GSAP + NextJS:

  • Exquisite Wood Floors - every animation was achieved with GSAP, my favorite animation is the wood tile page transitions.

  • Finnish Interiors - Loading screen animation & every animation was achieved with GSAP

  • Blockhead Digital - this is my freelance digital growth company - was able to achieve a loading screen animation and animate a ThreeJS object using GSAP.

Check out what other developers are creating with GSAP. These animations are insane, I spent hours scrolling - https://greensock.com/showcase/

In this tutorial, we will show you how to set up GSAP animations on NextJS 13, a popular framework for building server-rendered React applications. By the end of this guide, you'll be able to create smooth, performant animations that will take your NextJS projects to the next level.

Setting up GSAP on NextJS

To start, we're gonna create our NextJS 13 project using the following command:

npx create-next-app@latest project-name

Change project-name to whatever you feel appropriate - I used gsap-demo. You're then going to see a few prompts asking you for your project preferences, select whatever settings you see fit. I've opted for the following settings:

✔ Would you like to use TypeScript with this project? … No
✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use `src/` directory with this project? … No
✔ Would you like to use experimental `app/` directory with this project? › No
✔ What import alias would you like configured? … @/*

I went ahead and installed TailwindCSS for styling because it's the best thing ever. Follow this guide to install TailwindCSS in your project.

I also removed the default styling in global.css and elements in index.js page to get a blank page:

export default function Home() {
  return (
    <>
      <main>
        <div>Hello</div>
      </main>
    </>
  );
}

Now run the following command to install GSAP in your project:

npm i gsap

You now have GSAP installed in your project - you're now ready to create beautiful animations.

Basic Animations

I created a simple red cube which we'll animate throughout this article. To start we need to import gsap into our project. I imported it into index.js.

import gsap from "gsap";

export default function Home() {
  return (
    <>
      <main className="h-screen w-screen flex justify-center items-center">
        <div className="w-20 h-20 bg-rose-600"></div>
      </main>
    </>
  );
}

According to information found on the Greensock website (greensock.com), accessing DOM nodes and using Refs, in combination with the useLayoutEffect hook, is the recommended method for targeting elements and running GSAP animations in React/NextJS applications. However, I haven't had any issue with the useEffect hook to run animations - find whichever works best for you.

gsap comes with various useful methods for animating elements. We'll be using the to() method.

The most common type of animation is a to() tween because it allows you to define the destination values (and most people think in terms of animating to certain values):

// rotate and move DOM node ref element
// ("x" is a shortcut for a translateX() transform)
// over the course of 1 second.
gsap.to(boxRef.current, {rotation: 27, x: 100, duration: 1});

https://greensock.com/docs/v3/GSAP/gsap.to()

With useRef, useLayoutEffect Hook and gsap.to() in mind, here's an example of a simple GSAP animation in NextJS 13:

import { useRef, useLayoutEffect } from "react";
import gsap from "gsap";

export default function Home() {
  const boxRef = useRef();

  //useLayoutEffect ensures that elements are rendered and ready to be animated
  useLayoutEffect(() => {
    // Refs allow you to access DOM nodes
    console.log(boxRef); // { current: div.box }
    // then we can animate them like so...
    gsap.to(boxRef.current, {
      // rotate 360
      rotation: "+=360",
      //repeat forever
      repeat: Infinity,
    });
  }, []);

  return (
    <>
      <main className="h-screen w-screen flex justify-center items-center">
        <div ref={boxRef} className="w-20 h-20 bg-rose-600"></div>
      </main>
    </>
  );
}

🎉 Congratulations! 🎉 You have created your first GSAP animation on NextJS 13!

Feel free to play around with the values and different properties - you can animate any CSS property you can think of! Test out gsap.from() and gsap.fromTo() - you should be able to guess what kind of animations you can achieve with those methods.

Animating multiple elements

What if you have multiple elements you want to animate? It would be annoying if we had to create a Ref for every single element. This is where gsap.context() comes in!

gsap.context() provides two incredibly useful features for React developers, the option of using scoped selectors and more critically - animation cleanup.

https://greensock.com/react/#context

To start, all we need to do is provide gsap.context() a scope Ref - I usually place it on the parent container of the component I'm animating. This allows us to use selector text (like ".my-class" or "#my-id") to animate descendent elements. We don't need Refs for every element - we just need to provide an id, classname or other attributes to identify our elements, they're similar to CSS selectors.

let ctx = gsap.context(() => {
    //animations
    gsap.to("#apple", { // animate elements w/ id="apple"
        y: 10,
    })
    gsap.to("#orange", { // animate elements w/ id="orange"
        x: 10,
    })
}, scopeRef) //parent scope

Cleaning Up

useLayoutEffect() provides us with a cleanup function that we can use to kill animations. Proper animation cleanup is crucial to avoid unexpected behaviour with React 18's strict mode. This pattern follows React's best practices.

gsap.context makes cleanup nice and simple, all GSAP animations and ScrollTriggers created within the function get collected up so that you can easily revert() ALL of them at once.

We can also use this cleanup function to kill anything else that could cause a memory leak, like an event listener.

https://greensock.com/react/#context

I've added two boxes to my previous example, added the Ref to the parent container and added an id attribute to all three of the boxes:

import { useRef, useLayoutEffect } from "react";
import gsap from "gsap";

export default function Home() {
  const ref = useRef();

  useLayoutEffect(() => {
    let ctx = gsap.context(() => {
      gsap.to("#box", {
        rotation: "+=360",
        repeat: Infinity, 
      });

      gsap.to("#box1", {
        y: -100,
        yoyo: true, // animates from start -> end -> start
        repeat: Infinity,
      });

      gsap.to("#box2", {
        scale: "1.5",
        yoyo: true,
        repeat: Infinity,
      });
    }, ref); //scope

    return () => ctx.revert(); // cleanup
  }, []);

  return (
    <>
      <main
        ref={ref}
        className="h-screen w-screen flex justify-center items-center gap-10"
      >
        <div id="box" className="w-20 h-20 bg-rose-600"></div>
        <div id="box1" className="w-20 h-20 bg-rose-600"></div>
        <div id="box2" className="w-20 h-20 bg-rose-600"></div>
      </main>
    </>
  );
}

Conclusion

Setting up GSAP on NextJS is straightforward as installing any other package - the implementation can be a little tricky, but now that you have your first animation up & running, you can start your journey into creating more advanced animations, like scroll-based animations using ScrollTrigger, complex sequence animations using Timeline's, and more!

Here are a few resources to help with your GSAP journey:

Here's the source code for this project.

P.S. Thank you for reading my first blog post - this will be the first of many.

Did you find this article valuable?

Support Luke Vakasisikakala by becoming a sponsor. Any amount is appreciated!