GSAP with Next.js and Sitecore XM Cloud

Guide to get your first scroll-triggered smooth animation.

July 11, 2025

By Hugo Miranda

Animation That Elevates the User Experience

Modern web experiences rely heavily on visual storytelling, fluid transitions, and responsive interactivity. Whether you're using Sitecore XM Cloud to build a landing page, a product showcase, or a dynamic single-page application, animations can make or break user engagement. Improving UX allows easier navigation for the user, guiding them through the motion and giving immediate feedback while they interact with elements such as buttons, links and dynamic sections. All of this combined increases user engagement with the website which impacts directly on key business aspects such as sales, interaction numbers and branding positioning, giving a better lasting impression of the brand, company or store to be more modern and professional.

While CSS animations are great for simple transitions, when it comes to crafting rich, interactive experiences with fine-grained control, it is recommended to use JavaScript libraries such as:

  • motion.dev (previously Framer Motion)
  • Three.js (Specific for 3D animations)
  • GSAP (GreenSock Animation Platform)

Here’s a table comparing some of these options with added rankings, considering 1 to be the best and 3 to be the worst.

Comparison Between Animation Options

GSAP motion.dev CSS animations
Performance 3 - Good 2 - Better 1 - Best (native)
Bundle size 3 - Large: ~58 KB (core only) 2 - Small: ~9–13 KB (core only) 1 - None: 0 KB (native)
Animation Sequence Control 1 - Great 2 - Limited 3 - None
Scroll-based Animations 1 - Via ScrollTrigger plug in 2 - In Beta at the moment 3 - Possible with JavaScript
Handling Complex Animations 1 - Best 2 - Good 3 - Not recommended
Ease of use 3 - Steep learning curve 2 - Softer learning curve 1 - Easy to learn the basics
Popularity 2 - Widely used 3 - Growing 1 - Browser-native

Although motion.dev has been catching up lately in terms of animation complexity while offering the great performance and ease of use packaged up in a small-sized bundle, GSAP has established itself as the go-to JavaScript animation library for front-end developers and motion designers looking to build complex and rich animations with great control options, a large plugin ecosystem and a wide support network of developers helping each other through blogs and tutorials online.

In this blog, we’ll take a hands-on approach to understanding how GSAP works, how to integrate it with React and Next.js, and finally how to use it alongside Sitecore XM Cloud and the Sitecore JavaScript Rendering SDK (JSS). Whether you're new to GSAP or just need a refresher, this post is meant to provide a practical, real-world introduction that leads to a production-ready animation workflow.

What is GSAP?

GreenSock Animation Platform, commonly known simply as GSAP, describes itself as a robust framework-agnostic animation JavaScript library. This platform gives developers a high level of control over the animation of DOM elements with great performance. GSAP offers a wide range of animation tools, from basic transitions like size, opacity, color, and position changes, to more advanced effects such as morphing text and SVGs, building complex dynamic user interfaces, or synchronizing multiple animations with user scroll behavior. Numerous websites across the internet demonstrate its impressive capabilities for creating engaging user experiences. If you're curious about what libraries like GSAP can achieve, the GSAP page on the Awwwards site is an excellent place to explore. There, you'll find many standout examples of web animations powered by GSAP, sometimes including other cutting-edge UI libraries as well.

Being framework-agnostic means that it works out-of-the-box with any of the popular JavaScript frameworks most developers are using at the moment, such as Angular, Vue and React, or even Vanilla JS. Since the focus of this blog is to work with Next.js, a framework built on top of React, and the Sitecore JavaScript Rendering SDK for Sitecore XM Cloud, we will explain how to use GSAP with React, especially with it being the framework out of that list that needs some extra configuration to work well with GSAP due to the way its life cycle hooks work. This step-by-step approach will help establish a solid foundation before we move on to implementing animations in the context of a Sitecore XM Cloud project using NextJS.

Adding GSAP to a Next.js Project

In order to get started, first we need to install the GSAP module for React on our NextJS project using npm install @gsap/react. Once this dependency has finished installing, you can go ahead and import both the gsap instance and the useGSAP hook on the file you intend to add animations. Remember to also register the hook as a plugin to avoid version discrepancies.

import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
gsap.registerPlugin(useGSAP);

Assuming you are already familiar with React, you know the hooks are key to make code work with component rendering the way it was intended. In the past, this caused some issues with different third-party libraries like GSAP, but that’s why they created the useGSAP(), which as they describe it on their official resources section for React, is a hook that basically replaces hooks like useEffect() or useLayoutEffect() and handles cleanup using gsap.context(). This means that when a component is unmounted, GSAP cleans up after itself automatically avoiding the issues that otherwise leftover code could cause. Additionally, this hook can take the config object as an extra parameter to add configuration such as the list of dependencies, exactly like React’s useEffect hook, or the scope, where we can define the limitations of reach in the page for that specific GSAP code.

Basically, now the only thing you need to do is to add this hook where you can place your GSAP code in, as such:

useGSAP(() => {
    // gsap code here...
}

With this initial configuration in place, you're ready to begin integrating GSAP animations within your project. From here, we’ll explore how to apply animations to elements within your React components, starting with simple transformations and gradually introducing more advanced concepts like Timelines and scroll-based triggers.

TL;DR

  1. Install npm install @gsap/react
  2. Add both imports in your tsx file
    1. import gsap from 'gsap';
    2. import { useGSAP } from '@gsap/react';
  3. Register the useGSAP plugin
    1. gsap.registerPlugin(useGSAP);
  4. Add the useGSAP hook where you can place your gsap code

How Does GSAP Work?

As previously mentioned, GSAP is a powerful and versatile animation library that provides an extensive set of tools for building high-performance, interactive web experiences. While a full exploration of its capabilities is beyond the scope of this blog, we’ll focus on the core concepts and highlight a few advanced examples. This foundational understanding will then be applied in a practical context using Sitecore XM Cloud. Before we jump into that real example below in the article, there are three main GSAP concepts you must understand: Tweens, Timelines and the ScrollTrigger plugin.

1. Tweens, the GSAP core building blocks

Fundamentally, GSAP animations work by dynamically adding smoothly-transitioned in-line styling to the elements identified by a given selector (a class, an id or even a reference) using the same styling properties we as developers are already used to from a CSS file. Let’s take a look at the easiest example GSAP gives on their documentation:

gsap.to('.box', {x: 200})

This simple line of code above can be broken up into three parts: a method, a target(s) and vars. Right next to the gsap module, we start by seeing the method to, which will take any current styling the target element already has to a new state, configured by the vars. This method is part of the Core functionality from GSAP, which also includes the methods from, fromTo and set which we will explain later. The target element works as a CSS selector, which in this particular case is targeting any element that has the box class. You can use pretty much any styling property that can be animated, but if you want to see all the different properties that can be set on the vars in detail, you can take a look at the CSS Fundamentals section on their documentation.

When you use one of GSAP’s core animation methods, such as to, from, or fromTo, you're creating what GSAP calls a Tween. A Tween is an object return by any of the core methods that stores all the details of that animation instance, including the target element, duration, easing, and final state. More importantly, Tweens provide built-in controls and lifecycle events that allow for fine-tuned interaction and control.

For example, you can easily pause, resume, or reverse a Tween, or hook into lifecycle events like onStart, onUpdate, or onComplete to run custom logic at specific moments during the animation.

const myTween = gsap.to('.box', { x: 100 });
// Later in your code
myTween.reverse(); // Reverses the animation back to its original state

This level of control is particularly useful when your animations are dependent on user interactions or conditional logic. Unlike CSS animations or transitions that run once and forget, GSAP Tweens give you persistent, programmable animations that you can manage throughout the component's lifecycle.

2. Configuring Multiple Animations with Timelines

While Tweens are a great way to animate elements on their own, most real-world use cases require sequencing multiple animations to make it interesting. This is where Timelines come into play. Think of a GSAP Timeline as choreography instructions that allows you to coordinate multiple Tweens in a precise order. You can then control the entire sequence of animations as a single unit—play, pause, reverse, or scrub through it—all while maintaining total control over the timing and overlap of each animation.

const tl = gsap.timeline({ paused: true });
tl.to(".square-1", { x: 500, duration: 2 })
  .to(".square-2", { scaleX: 0 }, "<") // starts at the same time as previous
  .to(".square-3", { scale: 2, duration: 0.3 }, 0.5) // starts 0.5s after timeline begins
  .to(".text-1", { autoAlpha: 1, scale: 3 }, "-=0.5"); // starts 0.5s before the previous one ends
tl.resume(); // Start playing the timeline

There are a few important concepts to keep in mind when working with GSAP Timelines. One of the most powerful tools at your disposal is the position parameter. This feature allows you to precisely control when each animation starts relative to others in the sequence. Whether you're using numeric offsets like "0.5" to start half a second into the timeline, the "<" symbol to align with the start of the previous animation, or relative values like "-=0.5" to begin slightly before the previous one ends, this flexibility makes complex motion designs significantly easier to manage. Timelines also allow you to chain as many .to() ,.from() or .fromTo() method calls as needed, and if your animation logic gets more involved, you can even nest one timeline inside another for deeper control and modularity. Additionally, GSAP Timelines support a wide range of lifecycle callbacks such as onStart, onComplete, and more, giving you the ability to run custom logic at specific points during the animation flow—whether you're triggering UI changes, starting new animations, or logging analytics events.

3. Adding the ScrollTrigger Plugin

With a Timeline in place, the next step is elevating the user experience by adding ScrollTrigger, one of GSAP’s most powerful and widely-used plugins. This plugin connects animations to the scroll behavior of your page, allowing you to control exactly when an animation starts, how it progresses, and what happens based on the scroll position. In short: ScrollTrigger is what turns static animations into interactive storytelling. Here’s a practical example of using ScrollTrigger plugin on a Timeline:

// Remember to import and register the ScrollTrigger plugin
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
let tl = gsap.timeline({
  scrollTrigger: {
    trigger: ".container",  // the element that triggers the animation
    pin: true,              // keeps the element pinned during the animation
    start: "top top",       // when the top of the trigger hits the top of the viewport
    end: "+=200",           // animation runs over 200px of scroll
    scrub: 1,               // links animation progress to scroll progress
    snap: {
      snapTo: "labels",     // optional: snaps to the nearest label
      duration: { min: 0.2, max: 3 },
      delay: 0.2,
      ease: "power1.inOut"
    }
  }
});
tl.to("#card-1", { x: -5, y: 150, rotation: -31 })
  .to("#card-2", { x: 50, y: 200, rotation: 12 }, "<0.1")
  .to("#card-3", { x: 200, y: 100, rotation: 87 }, "<0.1")
  .to("#card-4", { x: -350, y: 25, rotation: -41 }, "<");

With just a few lines of code, we’ve connected a multi-step animation to the scroll position, pinned an element in place, and configured smooth snapping to different animation states. This approach not only improves storytelling and visual interest, but also allows your page to react more intuitively to the user’s input. ScrollTrigger effectively bridges the gap between static content and immersive interaction—and is often the key differentiator that makes GSAP animations stand out in award-winning websites.

So far, we have covered what is GSAP, how to install it and work with it on a React/NextJS project and how to start configuration different types of animations. Now it’s time to put it all together on a project using XM Cloud. In this example I used the PLAY!Summit XM Cloud demo as a playground to test GSAP with different components. You can find this and other demo sites, as well as instructions on how to use it, in the Sitecore Demo Portal.

Using everything we covered so far, I created a quick example that puts everything together and applies it to elements on a NextJS project that gets its content from Sitecore XM Cloud. In this particular case, the image and the text on this hero banner. Regardless if the content is changed by an author in the future, these animations will remain attached to the elements.

The goal is to add some simple animations to the elements on the Hero section using the ScrollTrigger plugin from GSAP. First we need to assign the ScrollTrigger configuration to a Timeline. For this example we will configure five of the ScrollTrigger properties:

  1. trigger - which element will start the animation; in this case it will be the .hero-section container.
  2. start - at what point of the scroll in relation to the trigger will that happen; in this example we want the animation to start exactly when the top of our trigger enters the top 10% of the screen.
  3. end - where will the animations of the Timeline end; here we will add 250px worth of scroll after the animation has been triggered
  4. pin - whether to briefly fix the trigger container while the animations happen or not, in this case we set this to false.
  5. scrub - the behavior that allows to play the animations in reverse while scrolling back and forth, we can either set it to true or false, but if we add a number to it, it also configures the amount of time the animation takes to “catch up” with the scroll; in this example we set it to 1 second of smooth delay.

Once the Timeline has the ScrollTrigger configuration set, we can then add the desired animations to it. In this example we will just change the opacity of both the text items and the background. An easy way to target the JSS components is using their className, for example, in this project, all the text elements inside the Hero section have the .hero-gsap-text class while the background with the Image component has the .hero-background class (This could also be done by using React Ref). In both cases, they would go to zero opacity when scrolling out, while the text elements will also be moving 100px on the x axis, making sure this is staggered 0.2 seconds between all the text elements. By adding the position parameter to ‘<0.1’ on the background animation we can ensure it happens slightly after the animations for the text elements. Remember that this code needs to be within the useGSAP hook we talked about before. Here is the complete code below:

  // The useGSAP hook with its code goes inside the component file you plan to animate
  useGSAP(() => {
    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: '.hero-section', // Uses the hero-container position to trigger the animations
        start: 'top 10%', // The animations will trigger when the top of this container is at 10% of the height of the screen
        end: '+=250', // In this example, we will complete the animation 250px of scroll later
        pin: false, // pin the trigger element while active
        scrub: 1, // smooth scrubbing, takes 1 second to "catch up" to the scrollbar
      },
    });
    tl.to('.hero-gsap-text', { opacity: 0, x: 100, stagger: 0.2 }) // This will target all the elements that have this class and stagger the animation between them
        .to('.hero-background', { opacity: 0 }, '<0.1'); // The <0.1 position parameter sets the opacity animation 0.1s after the previous animations start
  });
  // This is a simplified JSX on how some of the elements were displayed in the project so you get the idea of the HTML structure
  return(
      <>
      <section className="hero-section">
        <div className="hero-background">
          <Image field={props.fields.Hero} />
        </div>
        <div className="hero-container">
          <div className="container-content">
            <div className="content-text">
              <Text field={props.fields.Slogan} tag="p" className="slogan hero-gsap-text" />
              <Text field={props.fields.Eyebrow} tag="h1" className="expo hero-gsap-text" />
              <Text field={props.fields.Title} tag="h3" className="title hero-gsap-text" />
              <RichText field={props.fields.Body} tag="div" className="subtitle hero-gsap-text" />
            </div>
          </div>
        </div>
      </section>
      </>
  )

And in this gif below you can see how the resulting animation looks like:

GSAP GIF animation

Your Next Steps with GSAP

GSAP, as mentioned previously, is a robust resource with an large number of features that allow for endless different applications for animations in any web development project. The best way to take advantage of a library like this is to understand how the basics work and build up from there. The purpose of this simple example is to dip your toes and see how you like it, and if you are interested in continue the journey with GSAP animation, you can try to extend this exercise and add different animation Timelines to other sections that are triggered by different containers. I would also recommend checking out one of the GSAP CodePen examples, fork it and play around with the configuration to modify it to your liking, or if you would like a bigger challenge, you could pick one of your favorite animation sequences from a website featured in the Awwwards GSAP page and try to replicate it on your own from scratch.

Having an impactful website nowadays relies heavily on the details, the quality in their development, the care and thought put into its craftsmanship that go above and beyond to deliver a not just a good user experience, but a great one. In a time where the amount of sites to visit on the internet are seemingly endless, leveraging tools like these animation libraries can be a great asset for a lot of projects and companies that want to stand out, and get to the next level, not simply for a wow-factor, but for their websites to inspire trust and confidence in the end-user.

Helpful Resources

Photo of Fishtank employee Hugo Miranda

Hugo Miranda

Front End Developer

Hugo is a Front-End Developer with 6+ years of experience in consulting agencies, delivering web solutions for multinational corporations, mid-sized businesses, and startups. Apart from coding, playing the guitar has always been a big part of his life since he was a kid and when he's not at home, you’ll find him hiking or playing soccer in the summer and in the winter, trying to fall less from a snowboard.