GSAP & Next.js Setup: The BSMNT Way
DATE
CATEGORY
Development
READ TIME
9 minutes
VIEWS
...GSAP & Next.js Setup: The BSMNT Way
At basement.studio we're firm believers in streamlining our toolset to drive creativity and thrive strongly. And that's where Next.js and GSAP come into play. These tools are our weapons of choice to build creative and performant experiences in all our projects.
Here on our blog, we wanna share our experience because, let's face it, we have a blast building cool stuff with these tools. Take BSMNT Scrollytelling, our latest library. It's a prime example of what we've accomplished from investing time and effort in this particular stack.
Plus, there are some specific tips for pairing Next.js with GSAP that can make your life easier. Before jumping into our OSS library or other complex animation challenges, we'll start with the basics — project setup and our team's specific approaches.
WHY NEXT.JS AND GSAP
Next.js is our go-to framework, simplifying web development by providing a solid application structure, routing, and server-side rendering (SSR), not to mention a plethora of components that take the headache out of complex front-end infrastructure. Even though the framework is a key part of our toolkit, we won't be delving into fundamentals in this post. But stay tuned; we’re cooking something for upcoming content!
Interested in learning more about Next.js? Check out their official resources.
When it comes to bringing our creations to life, GSAP is our animation engine of choice. We know how it pairs with React—the good and the bad stuff. It's also brimming with features far beyond any other library and is full of powerful plugins we always vet before tackling a tricky animation problem. As a framework-agnostic library, you can use it whether you're working in React or any other framework. The core principles stay the same.
With GSAP, you can work with any web component. In Greensock's words, it lets you "animate anything" —whether it's simple DOM transitions, SVG, Three.js, canvas or WebGL, or even generic JavaScript objects.
And let's not forget, as they proudly state in their docs, animating imperatively with GSAP offers you a lot more power, control, and flexibility. Plus, GSAP is obsessed with performance, optimizations, and browser compatibility, so you can focus on the fun stuff.
This post is all about configuring GSAP in Next.js. We recommend a solid understanding of React and encourage you to check out the GSAP’s intro documentation.
THE BSMNT WAY
Now, let's get down to business. Where do we set up GSAP? You could put it anywhere on the app, but what if you are looking for consistency or easy maintenance? Instead, let's keep things tidy and organized.
Let’s start by generating a file within the lib/gsap
directory, where we will perform the imports and possible plugin registrations. Here we also will set the default values we want to use across the app, such as duration, ease, and other settings.
1// src/lib/gsap/index.tsx23import gsap from 'gsap';4import { CustomEase } from 'gsap/dist/CustomEase';5import { SplitText } from 'gsap/dist/SplitText';67gsap.registerPlugin(CustomEase, SplitText);89const GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;10const RECIPROCAL_GR = 1 / GOLDEN_RATIO;11const DURATION = RECIPROCAL_GR;12const EASE = CustomEase.create('ease', '0.175, 0.885, 0.32, 1');1314// Configuring GSAP with custom settings that aren't Tween-specific15gsap.config({16 autoSleep: 60,17 nullTargetWarn: false18});1920// Setting default animation properties that should be inherited by ALL tweens21gsap.defaults({22 duration: DURATION,23 ease: EASE24});2526// Once the desired configurations are set, we simply export what we need to work with in the future.27export { CustomEase, DURATION, EASE, GOLDEN_RATIO, gsap, SplitText };
In a nutshell, what we're doing in this file is setting up and customizing GSAP to fit our needs and preferences for the project. Now, let's go over the key parts of the code and their purposes:
Basic import
1import gsap from 'gsap';2import { CustomEase } from 'gsap/dist/CustomEase';3import { SplitText } from 'gsap/dist/SplitText';45gsap.registerPlugin(CustomEase, SplitText);
We're importing GSAP and some additional plugins, like CustomEase
and SplitText
. We’re not diving deep into this today, but plugins are often used for creating more advanced animations. Don’t forget to register them with gsap.registerPlugin()
to ensure seamless function with the core framework without tree-shaking issues in build tools/bundlers.
Constants
1const GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;2const RECIPROCAL_GR = 1 / GOLDEN_RATIO;3const DURATION = RECIPROCAL_GR;4const EASE = CustomEase.create('ease', '0.175, 0.885, 0.32, 1');
We're setting a series of constants that we'll use later. For example, DURATION
represents the default timing for animations, so you can tweak it here and propagate the value everywhere on your app, keeping consistency in your animation system.
Default configuration
1// Configuring GSAP with custom settings that aren't Tween-specific2gsap.config({3 autoSleep: 60,4 nullTargetWarn: false5});67// Setting default animation properties that should be inherited by ALL tweens8gsap.defaults({9 duration: DURATION,10 ease: EASE11});
In this section, we're setting up some custom options.
With gsap.config()
, you can tweak GSAP's settings that aren't Tween-specific.
Through gsap.defaults()
, we're setting the default properties of all animations by passing the constant values we previously defined.
Export
1// Once the desired configurations are set, we simply export what we need to work with in the future.2export { CustomEase, DURATION, EASE, GOLDEN_RATIO, gsap, SplitText };
Finally, we're exporting the relevant constants and objects so they can be used in other files. This facilitates consistency and reusability of configurations across the code.
THE KEY PART: CUSTOM IMPORTING
Now, here's the magic trick. Instead of importing GSAP the old-fashioned way, we're going to use import { gsap } from '~/lib/gsap'
instead of import gsap from 'gsap'.
Why? Because we're all about consistency, global management, and reusability. When we import GSAP from the ~/lib/gsap/index.tsx
file, we're using a GSAP instance that's already set up according to preferences and default values we've configured before.
This approach ensures all animations use the same settings, making the code more predictable and easier to maintain.
If we ever need to make changes to GSAP's configuration across the entire project, we only have to do it in one place. This simplifies global configuration management.
If we wish to reuse certain constants or settings in multiple components, like
DURATION
, we can easily do so by importing them from our preset file instead of having to define them again in each component.If we need to update GSAP or its plugins in the future, we only have to perform the update in one place rather than searching and modifying each individual import throughout the project.
WORKING IN REACT ENVIRONMENTS
It's worth noting that GSAP is framework-agnostic, so it's not specifically designed to work with any particular framework, including React. This means you can use GSAP with any front-end technology, making it incredibly versatile.
However, when using GSAP within a React (or Next.js) project, it's important to keep some key aspects in mind to ensure they work together efficiently and harmoniously. This includes handling the state, component lifecycles, and the way React updates the DOM.
Respect the React flow: For instance, you shouldn't use GSAP to directly modify React's state, but instead, use the setters provided by state hooks.
Proper DOM Handling: React has its own virtual DOM management, while GSAP works directly with the actual DOM. Make sure that when GSAP interacts with DOM elements, they're already present and managed by React. We’ll be diving deep into this while talking about the lifecycle.
Understanding Lifecycles: React handles its own component lifecycle, and it's important that your GSAP animations integrate with it.
Surviving in the lifecycle
The biggest challenge when working with animations in SSR apps like Next.js is making sure they play nice with the component's lifecycle. You might want to start animations after React has updated the DOM, right?
So, here comes useIsomorphicLayoutEffect
to the rescue.
1useIsomorphicLayoutEffect(() => {2 const tween = gsap.fromTo(...);34 return () => {5 tween.revert();6 };7}, []);
This hook is a blend of React's traditional effect (useEffect) and useLayoutEffect, designed to handle things both on the server and on the client. Whenever we use this, we shouldn't run into synchronization issues since it runs after DOM mutations have been made but before the browser has painted.
But what’s under the hood? It’s all about being aware of the window component.
1import { useEffect, useLayoutEffect } from 'react';23const useIsomorphicLayoutEffect =4 typeof window !== 'undefined' ? useLayoutEffect : useEffect;56export default useIsomorphicLayoutEffect;
Animating at the component level
Alright, enough with the configs and warnings. Let's get this party started and make something move on the screen. Let’s make a div
that plays a fade-in and slide-in animation when the component mounts. We’ll need to revert the animation when the component unmounts for cleanup. Easy stuff:
1export default function MyComponent() {2 const productRef = useRef<HTMLDivElement>(null);34 useIsomorphicLayoutEffect(() => {5 if (!productRef.current) return;67 const timeline = gsap.timeline({8 paused: true,9 defaults: {10 ease: 'power4.out'11 }12 });1314 timeline.fromTo(15 productRef.current,16 {17 autoAlpha: 0,18 xPercent: 10019 },20 {21 autoAlpha: 1,22 xPercent: 023 }24 );2526 timeline.play();2728 return () => {29 timeline.revert();30 }31 }, []);3233 return (34 <div ref={productRef} />35 );36}
First, we created a reference to a DOM element using useRef
, which targets the element we want to animate. In the render method, we can return a div
element and attach our reference to it. This element is what we'll animate with the GSAP timeline.
Next, we used the great useIsomorphicLayoutEffect
hook that runs synchronously after all DOM mutations. Inside this hook, it’s important to first check if the productRef.current
is defined. If it isn't, we return early to avoid errors.
Then, we created our GSAP timeline. We recommend you check out the official docs to learn everything about this powerful tool. Basically, by using timeline.fromTo()
, we defined the animation from a specific starting point, for instance, 0 for the autoAlpha
(making the element invisible) and 100 for the xPercent
(the horizontal offset). Then, it goes to an endpoint, where autoAlpha
must be 1 to reveal the element, and xPercent
needs to be 0 to appear in its original position. In simpler terms, this animation fades in the element while moving it horizontally from right to left.
Remember, there's no need to establish a duration or easing here, as we're applying the constant values we've already defined through gsap.defaults()
in '~/lib/gsap'
. This ensures a consistent rhythm across all components for a harmonious user experience. However, if you wish to deviate from these default values, you can manually overwrite them at this point.
Finally, with timeline.play()
, we can start the animation and then return a cleanup function that calls timeline.revert()
to reset the timeline to its initial state when the component unmounts.
Easy debugging
Lastly, we also want to have some control and good debugging experience. Especially when we're orchestrating a symphony of complex animations across multiple components. We're all familiar with the numerous methods available for debugging. These include the browser dev tools, the one and only console.log()
, and the powerful GSDevTools, to name a few.
But remember, you're operating within the lifecycle, so within the useIsomorphicLayoutEffect
, you can log the state of the timeline after it has played or even after it has been reverted. Double-check that the references to the DOM elements (using useRef in React) are correctly set up. Sometimes, animations fail simply because references aren't pointing to the right elements.
WHAT’S COMING
We're all set! Now you have a scalable codebase from which you can start building, maintaining, and tweaking with ease. We hope this approach proves beneficial to you. Do not hesitate to reach out to our team if you need any help - we're always open to discussing anything related to development on our Discord channel.
But don't think we're stopping here. We have exciting things lined up for you. Keep an eye out for upcoming chapters where we'll delve into more complex animation tasks. Plus, we'll get into BSMNT Scrollytelling, our open-source library for creating React-friendly on-scroll animations, all powered by GSAP's ScrollTrigger.
↖ Back to listing