Jump to content
Search Community

Elements are not rendering correctly on the page. Animation of components with server data. Next JS 14 App Router

Nazar Marusyk test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hello! In my Next JS application, there are three components on the main page: Skills, Services and Showcase. In all of these components, data is loaded from the server. All of them are imported into page.tsx using dynamic import
const Component = dynamic(() => import('@/components/component/Component').then(module => module.Component))

The problem is that the header markers appear immediately, without waiting for the elements inside the component to load and without waiting for the component itself to load dynamically.

The animations in these components are described in useLayoutEffect().

I can't figure out if this is a problem on the part of Next JS or GSAP. 
How can I postpone the rendering and animation of the headers until its parent component loads?

Here is the <MainTitle /> component.

<MainTitle />

Screenshot 2024-07-03 at 17.18.37.png

Link to comment
Share on other sites

  • Solution

Hi @Nazar Marusyk and welcome to the GSAP Forums!

 

First, based on the screenshot of your code this could be caused by a cleanup problem. When working with React we recommend using our useGSAP hook for better cleanup and in order to avoid some issues that stems from React's strict mode (since version 18 of React). You can learn more about it here:

https://gsap.com/resources/React

 

Second, since you're pulling data from your service, my guess would be that the height of the DOM elements inside the components changes once the data is loaded and properly rendered in the screen. For those cases is better to call ScrollTrigger.refresh() in order to tell ScrollTrigger to update the start and end positions of it's instances. Another option is to create the ScrollTrigger instances after the data from the server is loaded and reflected in the DOM. For that you can also use our useGSAP hook with a dependency array that contains the data. In combination with a useEffect hook with an empty dependencies array that makes the API call, should be enough. Something like this:

const [data, setData] = useState(null);

useEffect(() => {
  // Fetch the data here
  setData(response); // response would be the data from the server
}, []);

useGSAP(() => {
  // Run your code only if there's data from the server
  if (data) {
    // Create your ScrollTrigger and GSAP instances
    
    // After creating the GSAP and ScrollTrigger instances, call the refresh method
    ScrollTrigger.refresh();
  }
}, {
  dependencies: [data],
});

Finally if you keep having issues please create a minimal demo that clearly illustrates the problems you're having, you can fork one of our starter templates from our stackblitz colletion:

https://stackblitz.com/@gsap-dev/collections/gsap-nextjs-starters

 

Hopefully this helps

Happy Tweening!

  • Thanks 1
Link to comment
Share on other sites

I am very grateful to you, @Rodrigo. It has helped me a lot.

 

I would also like to know about gsap.context(). Is it a good practice to use it in every animated component? How does it affect SEO?

I am doing it now because in some cases, gsap doesn't find the trigger and wrapping the animation in gsap.context() solves this problem. 

I use gsap.context in conjunction with useLayoutEffect(). Is this the right approach in terms of practice and performance? 
Here is an example

Link to comment
Share on other sites

Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependencies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

 

Using a framework/library like React, Vue, Next, etc.? 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Hey!

 

Your useEffect in this demo fires twice, this is normal for React 18 - however this means that the prop 'data' gets updated twice, causing the useGSAP hook to run twice as it has data defined as a dependency.

 

In your case this means your 'from' tween in the hook is firing twice. 

 

- The first time it fires it tries to animate your elements in from 0 opacity (defined in the tween) to 1 opacity (which is the elements' current state) It starts the animation, setting the opacity on the element to 0

- Then the second time it fires it tries to do the from tween again, BUT this time because the 0 opacity has been set already, it tries to animate from 0 opacity (in the tween) to 0 opacity (which is the elements' current state) - so you don't see anything happening.

You can get around this by passing in revertOnUpdate: true

 

This reverts your elements to their pre animation state whenever the dependency array updates.

 

Hope this was helpful.

 

https://stackblitz.com/edit/nextjs-a77bua?file=app%2Fpage.tsx

 

 

 

 

  • Like 1
Link to comment
Share on other sites

Of course. I know about the nuances of React JS.
But they're not the only ones that are important to understand for smooth development and easier life. Right? 😉

 

I'm very grateful to you, Cassie.

revertOnUpdate: true solved my problem. 

 

The last thing I wanted to know was why the registered plugins in layout.tsx are not displayed in the components (the animation itself works, but a warning appears in the console).

Invalid property scrollTrigger set to {...}
Missing plugin? gsap.registerPlugin()

 

I register plugins in the root layout.tsx or in the root client ('use client') component - rootComponent.tsx

// src/app/layout.tsx

if (typeof window !== undefined) {
  gsap.registerPlugin(ScrollTrigger)
  ScrollTrigger.config({
    limitCallbacks: true
  })
}

// src/components/rootComponent/RootComponent.tsx

useLayoutEffect(() => {
  if (typeof window !== undefined) {
    gsap.registerPlugin(ScrollTrigger)
    ScrollTrigger.config({
      limitCallbacks: true
    })
  }
}, [])

 

After that, in any component, a warning appears in the console about the absence of the plugin (but again, the animation works)

// src/components/MainTitle.tsx

const titleEl = useRef<HTMLHeadingElement>(null)

useGSAP(() => {
  if (titleEl.current) {
    gsap.from(titleEl.current, {
      // ...
    })
  }
}, {
  dependencies: [],
  scope: titleEl
})


It's not critical, but I would be very grateful)

Link to comment
Share on other sites

Then I will do so. 
I thought there was a way to register plugins once, rather than registering them in every animated component.

 

You have helped me a lot, and I am very grateful to @Cassie, @Rodrigo and the entire @gsap community.
And thank you for what you do.

Good luck with your new developments😎


You will get more than you deserve😊

  • Like 1
Link to comment
Share on other sites

Absolutely. I use this approach.

But I still get warnings in some components. But everything works.

To avoid the warnings, I just re-register the plugin.

 

I'm worried about SEO and application performance.
If specifying plugin registration (gsap.registerPlugin()) in each animated component does not have a bad impact on optimization and is good practice, then there is no problem here. 

 

Thank you!

Link to comment
Share on other sites

Hi,

 

Those warnings normally are in dev mode, is always a good idea to create a production build and test that locally and see what you get there.

 

Normally you shouldn't get or care about warnings when registering the plugins inside an if statement checking for the window object:

if (typeof window !== "undefined") {
  gsap.registerPlugin(ScrollTrigger);
}

Also registering plugins is just a way to avoid tree shaking, nothing more, which is resolved on the build process not when the user gets to the production app. I can't really see how that could create a SEO problem TBH.

 

Happy Tweening!

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