Jump to content
Search Community

GSAP Scrolltrigger + NEXTJS 14 irratic behavior in dev mode

ppistecky test
Moderator Tag

Go to solution Solved by GSAP Helper,

Recommended Posts

I have a problem when using scrolltrigger with nextjs 14 on my local machine when running npm run dev. On the production site it works, but I can't develop like this.

I have a very basic example. Just when you create a next app with npx create-next-app@latestand this as page.tsx:

 

'use client';

import Image from 'next/image';
import { useLayoutEffect, useRef } from 'react';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';

export default function Home() {

  const backgroundImage = useRef(null);
  const foregroundImage = useRef(null);

  useLayoutEffect(() => {
    // get the height of the backgroundImage dom element
    console.log(backgroundImage.current.clientHeight);
    console.log(foregroundImage.current.clientHeight);


    gsap.registerPlugin(ScrollTrigger);
    const timeline = gsap.timeline({
      scrollTrigger: {
        trigger: document.documentElement,
        start: 'top',
        end: '+=500px',
        scrub: true,
      },
    });

    timeline
      .from(backgroundImage.current, {clipPath: "inset(15%)"});
  }, []);
 
  return (
    <main>
      <div>
        <div ref={backgroundImage}>
          <img src="/images/image.jpg" alt="xx" />
        </div>
      </div>
      <div ref={foregroundImage} className="h-[2000px]">
        hello
      </div>
    </main>
  );
}


on production (Vercel) this results in:

 

https://www.dropbox.com/scl/fi/1n1j9yqoz4uh2iln10uod/production.mov?rlkey=kkpe84c6259d6k4trqml3dq4q&dl=0


But on local dev:

https://www.dropbox.com/scl/fi/gljyff9ub7bmf6j96iym2/dev.mov?rlkey=8s9ec8ga1zz7mycogv2awrg9c&dl=0
 

Link to comment
Share on other sites

  • Solution

Hi @ppistecky and welcome to the GSAP forums!

Proper animation cleanup is very important with frameworks, but especially with React. React 18 runs in strict mode locally by default which causes your useEffect() and useLayoutEffect() to get called TWICE.

In GSAP 3.11, we introduced a new gsap.context() feature that helps make animation cleanup a breeze. All you need to do is wrap your code in a context call. All GSAP animations and ScrollTriggers created within the function get collected up in that context so that you can easily revert() ALL of them at once.

Here's the structure:

// typically it's best to useLayoutEffect() instead of useEffect() to have React render the initial state properly from the very start.
useLayoutEffect(() => {
  let ctx = gsap.context(() => {
    // all your GSAP animation code here
  });
  return () => ctx.revert(); // <- cleanup!
}, []);

This pattern follows React's best practices, and one of the React team members chimed in here if you'd like more background.

We strongly recommend reading the React information we've put together at https://gsap.com/resources/React/

 

Finally now we have the useGSAP hook that simplifies this quite a bit:

https://www.npmjs.com/package/@gsap/react

 

Happy tweening!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...