Jump to content
Search Community

ScrollTrigger in nextjs build not working reliably

andiwi test
Moderator Tag

Go to solution Solved by Cassie,

Recommended Posts

Hi @all,


thank you for this awesome library! Unfortunately, I am facing issues with the ScrollTrigger effect inside my nextjs production build. I hope you can help me.

 

The ScrollTrigger effect works perfectly fine, when I start the nextjs project in development ("next dev"). However, when I build a production build and host the production build via a web server, the element to which the ScrollTrigger effect is attached to is not displayed from time to time.

 

I created a simple codesandbox where you can reproduce the behaviour: https://codesandbox.io/s/nextjs-gsap-scrolltrigger-bug-qtgfl?file=/pages/index.js It works fine inside the codesandbox (development) but when built and deployed to a server https://csb-qtgfl-nml1dj1ep-andiwi.vercel.app/ the headline is not displayed from time to time. To reproduce go to https://csb-qtgfl-nml1dj1ep-andiwi.vercel.app/ and refresh the page multiple times until the headline is not displayed anymore. 

 

I followed the official resources https://greensock.com/react/ and https://greensock.com/st-mistakes/ and also read through forum posts like https://greensock.com/forums/topic/27012-scrolltrigger-and-array-of-refs-in-react/ but did not find a solution for the problem.

 

I am new to gsap. Do you spot any wrong usage of the library? The same setup works in a simple react app (e.g. create-react-app). Would be great if you have any ideas how to solve this error. I am trying to solve the error for days now without any success and I am out of ideas.

 

Thank you!

 

 

 

 

Link to comment
Share on other sites

Thank you for your quick response. I tried to use useLayoutEffect. However, when using the useLayoutEffect hook, the ScrollTrigger effect does not work at all.

Here is the example: https://codesandbox.io/s/nextjs-gsap-scrolltrigger-bug-uselayouteffect-vxxd9?file=/pages/index.js
Deployed: https://csb-vxxd9-andiwi.vercel.app/

EDIT: Indeed, what I can observe is that the console.log output is sometimes different, but the bug occurs in both cases. It looks like the DOM is not ready when gsap effect is initialised. 

console.log output (example 1):
image.thumb.png.70205700c48cbb6a6520dd5a45491443.png

console.log output (example 2):
image.thumb.png.7da9c82926210c13a7f456b66481c4a3.png
 

Link to comment
Share on other sites

This example seems to work on codesandbox. Not sure why it wouldn't be working for you?


I don't think you need to be killing the animation, and you had paused:trueset which isn't needed on a scrollTriggered animation. It will be paused anyway until it hits the trigger point.

https://codesandbox.io/s/nextjs-gsap-scrolltrigger-bug-uselayouteffect-forked-eu2v9?file=/pages/index.js

Link to comment
Share on other sites

Thank you for the hints. I removed the paused:true and the animation cleanup (although on this page a cleanup is recommended: https://greensock.com/react/#cleaning-up )

Yes, it works on codesandbox (which is the development setup), but when I build and deploy the production build the headline is not displayed.

Link to comment
Share on other sites

I just tested a gsap.from and a gsap.to with and without opacity animation (without scrollTrigger). 
For example:

useLayoutEffect(() => {
    gsap.from(h1Ref.current, {
      rotation: 45,
      y: 200,
      duration: 1,
      opacity: 0, // also tested without this line
    });
  }, []);

All examples are working fine in production build. Maybe an issue in the scrollTrigger plugin?

EDIT: I also tested the same effect with my scrollTrigger which works fine with a gsap.to but when using the gsap.from in combination with scrollTrigger the animation doesn't get played correctly. So it might be a bug of scrollTrigger in combination with a gsap.from ?

 

useLayoutEffect(() => {
    gsap.to(h1Ref.current, { // gsap.to is working fine, gsap.from -> not working 
      scrollTrigger: {
        id: 1,
        trigger: h1Ref.current,
        start: "top bottom",
        toggleActions: "play none none reverse",
      },
      rotation: 45,
      y: 200,
      duration: 1,
      opacity: 0, // also tested without this line
    });
  }, []);

 

Edited by andiwi
additional info
Link to comment
Share on other sites

I doubt it because this shows the h1 is being animated. See the inline opacity and transform style.

 

image.png

 

I also noticed that you are animating the trigger, which you shouldn't do as that will mess up ScrollTrigger's calculations. Try animating a wrapper element instead.

 

  • Like 1
Link to comment
Share on other sites

I set the trigger to a wrapper div. Unfortunately, this didn't solve the issue. Is setting the trigger to the to-be-animated element bad in general when using ScrollTrigger with a gsap.from? I just followed the simple example of https://greensock.com/docs/v3/Plugins/ScrollTrigger 

 

What is interesting: when I wrap the gsap animation into a timeout to push it to the JS event loop, the animation at least gets played most of the time, but still not every-time (refresh browser multiple times until the animation gets stuck again). So there might be some race condition between DOM painting of react/nextjs static rendering optimization and gsap initialisation.

Example with scroll trigger on a wrapper div:  

 

EDIT: I once again tested the code with simple react -> also not working: https://codesandbox.io/s/react-gsap-scrolltrigger-not-working-gy3kn

Edited by andiwi
add simple react example
  • Like 1
Link to comment
Share on other sites

Oh no don't worry about that. Very happy to help! ☺️ Thank you for the kind offer though.

To answer this too...
 

Quote

I set the trigger to a wrapper div. Unfortunately, this didn't solve the issue. Is setting the trigger to the to-be-animated element bad in general when using ScrollTrigger with a gsap.from

 

It's a bad plan if you're going to be moving the trigger element as it can mess up calculations - if you're just doing an opacity fade it'll be fine.

  • Like 3
Link to comment
Share on other sites

  • 2 months later...
  • 1 year later...

Sure, GSAP and ScrollTrigger work great with Next. If you need help, please post a minimal demo that clearly illustrates the problem. Here is a starter template you can fork: 

https://stackblitz.com/edit/nextjs-5cm4qn


I bet you're just not doing proper cleanup which is essential in React. Please read https://gsap.com/react

 

You might also want to check out the brand new useGSAP() hook that we'll be announcing soon: https://www.npmjs.com/package/@gsap/react

Link to comment
Share on other sites

  • 1 month later...

I am having serious issues with GSAP scroll trigger + NextJS 14.1.0. In storybook it works as you would expect, markers are in the correct place etc. As soon as I run the same components in my nextj app dev or production... My start & end markers are in totally different places with the same settings when rendered in storybook vs nextjs.

 

You can see the start and end markers are overlapping in my next js version and the storybook version the start is where I would expect it to be

Screenshot 2024-01-23 at 02.28.51.png

Screenshot 2024-01-23 at 02.29.04.png

Link to comment
Share on other sites

3 hours ago, JPPdesigns said:

I am having serious issues with GSAP scroll trigger + NextJS 14.1.0. In storybook it works as you would expect, markers are in the correct place etc. As soon as I run the same components in my nextj app dev or production... My start & end markers are in totally different places with the same settings when rendered in storybook vs nextjs.

 

Howdy, @JPPdesigns. It's pretty tough for us to troubleshoot blind - would you mind providing a minimal demo (like a Stackblitz) that clearly illustrates the issue? I wonder if you're not doing proper cleanup or if you are doing routing in such a way that doesn't fire a "load" event which would trigger the ScrollTrigger.refresh(). You could just make sure you call a ScrollTrigger.refresh() when your content loads and is finished shifting the DOM layout around. 

 

Here's a Stackblitz you can fork: 

https://stackblitz.com/edit/nextjs-5cm4qn

 

Are you using the new useGSAP() hook? And the latest version of GSAP? 

Link to comment
Share on other sites

Sorry @GreenSock really hard to create a stackblitz for this issue as there is SO much setup to give you a minimal demo... What I noticed is if I have a dependency array set and pinSpacing: false then the animation is all out of whack. I am on the latest version of everything

 

With pinSpacing false in nextjs

image.thumb.png.826a9e1bec1c499004352e88b07a4486.png

 

with pinSpacing false in Storybookimage.thumb.png.f49a648863f1f32c9dbab9a8fd98d6a1.png

Link to comment
Share on other sites

Are you using the useGSAP() hook? My guess is you're not doing proper cleanup. Did you try setting revertOnUpdate: true in the config object? I'm curious why you'd have a dependency Array for something with a ScrollTrigger. Again, it's super duper hard for us to troubleshoot blind like this. 

Link to comment
Share on other sites

Yeah I am using the useGSAP hook. Below is my code snippet that I have ended up with which appears to work in my production build now but not in dev mode. Could dev mode be impacted by useEffect running twice in strict mode?

useGSAP(
    () => {
      matchMediaRef.current = gsap.matchMedia().add(
        {
          isMobile: '(max-width: 768px)',
          isDesktop: '(min-width: 769px)',
        },
        (context) => {
          const { isMobile } = context.conditions || {};
          gsap.set(firstViewRef.current, {
            opacity: 1,
            lazy: false,
          });
          gsap.set(scrolledViewRef.current, {
            lazy: false,
            opacity: 0,
            yPercent: 0,
            scale: 0.7,
          });

          timelineRef.current = gsap
            .timeline({
              scrollTrigger: {
                trigger: containerRef.current,
                start: 'top 44px',
                end: 'center 44px',
                invalidateOnRefresh: true,
                pin: true,
                pinSpacing: false,
                scrub: true,
                markers: isDev,
              },
            })
            .to(firstViewRef.current, {
              ...(isMobile
                ? {
                    yPercent: -40,
                    xPercent: -30,
                  }
                : {
                    yPercent: -38,
                    xPercent: -15,
                  }),
              opacity: 0.8,
              scale: 0.3,
            })
            .to(
              scrolledViewRef.current,
              {
                opacity: 1,
                scale: 1,
                yPercent: 0,
                xPercent: 0,
              },
              0.2
            );
        }
      );
    },
    {
      scope: containerRef,
      revertOnUpdate: true,
    }
  );

 

Link to comment
Share on other sites

Hi,

 

3 hours ago, JPPdesigns said:

Could dev mode be impacted by useEffect running twice in strict mode?

Nope, useGSAP takes care of animation cleanup for you so that shouldn't cause any issues.

 

Do you have a minimal demo that clearly illustrates the problem you're facing? Honestly I don't see anything in that code that should break on development but work on production.

 

The only thing that looks odd is the fact that you have revertOnUpdate: true but you don't have any dependencies array in your useGSAP hook, revertOnUpdate is to revert everything when one of the dependencies is updated, but for that you actually need a dependencies array.

 

Happy Tweening!

Link to comment
Share on other sites

Yeah it was left in from a previous implementation with a dependency array. Good news is this setup is working as expected in production. I wish I could post a video showing what it looks like in dev mode.

 

A minimal demo is pretty time consuming so I will try to create one in the coming days but now the production version is working as expected 😅 AND I can test in storybook as opposed to nextjs dev mode. I guess we are in a better place. I will remove revertOnUpdate too. Will get back to you with a stackblitz 

  • Like 1
  • Thanks 1
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...