Jump to content
Search Community

ScrollSmoother doesn't recognise content element in Next JS 13

Market South test
Moderator Tag

Recommended Posts

Hi there, 

 

I am working on a Next V13(using App Router) project and tried to use ScrollSmoother for smooth scrolling of the page.

 

But unable to make it work. On checking the console, can see a warning of ScrollSmoother needs a valid content element.

The screenshot is attached. 

 

We used ScrollSmoother on Next V12 which is working seamlessly. I followed the same coding standard on the new project. 

 

Due to the privacy concern of the project, I cannot create a CodePen example.

 

Example coding as below to give an idea:

 

app/layout.js

'use client'
import './css/globals.css'
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
import { ScrollSmoother } from "gsap/dist/ScrollSmoother";
import { useRef, useEffect, useState } from "react";


export default function RootLayout({ children }) {


  gsap.registerPlugin(ScrollTrigger, ScrollSmoother);

  useEffect(() => {

    ScrollSmoother.create({
      content: "#smooth-content",
      smooth: 3,
      effects: false,
      normalizeScroll: true,
    });


  }, []);

  return (
    <html lang="en-nz">
      <body suppressHydrationWarning={true}>
        {children}
      </body>
    </html>
  )
}

 

/app/page.js

export default async function Home() {

return (
<main className="tw-relative">
    <NavBar navData={navData} />  {/* fixed element...*/}
    <div id="smooth-wrapper">
        <div id="smooth-content">
          {/* rest of code...*/}
        </div>
    </div>
</main>
)
}

 

Any help would be appreciated.

Capture.PNG

Link to comment
Share on other sites

Hm, that error probably means that when you create your ScrollSmoother, there is no "#smooth-content" element in the DOM. Try adding this right before the ScrollSmoother.create(): 

console.log(document.querySelector("#smooth-content")); // ?

 

I'm not at all familiar with Next.js, but my guess is that it's calling that useEffect() once server-side, and then again after hydration(?), so the first time there isn't a corresponding DOM element yet. So ScrollSmoother is getting null as the content element, hence the error. Again, I'm completely guessing here because you didn't provide a minimal demo.

 

I wonder if you used a ref instead of selector text, maybe Next.js handles that better(?)

 

If you'd like more help, please provide just the most basic demo possible - no need to share anything about your actual project. It's actually much better if you just use some colored <div> elements and the most basic GSAP code possible to show the problem.

 

Here's a starter template that you can fork. 

 

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

@Rodrigo thank you for sharing the example. It works perfectly when the content element is placed in the root layout as in your example with a fixed element placed outside of the content element. 

 

But in my case, I have to pass a dynamic array value from API to the NavBar component where it loops through them as menu links. I cannot use the asyncfunction that uses awaitin the root layout as it throws lots of errors.

 

So as a workaround, I have declared the content element in the page component as in my query. 

 

Please correct me if I am wrong.

Link to comment
Share on other sites

Hi,

 

Without a minimal demo there is not much I can tell you, but most likely the navbar component would be outside the ScrollSmoother wrapper. Now if it's inside the smooth wrapper and maybe you're getting incorrect heights for your measurements, maybe an option would be to create your navbar with a dummy hidden link element that gives the normal height to the navbar and replace that with the real content once the API data is rendered correctly.

 

Happy Tweening!

Link to comment
Share on other sites

Hi, 

 

Thanks for the update. 

 

I have created a minimal demo demonstrating my query. 

 

In the example, you can see I have declared an array const called navigationin the layoutfile which I am passing on to the Navbarcomponent where the array is lopped as a menu link. 

 

In production, we need to get the dynamic array value from an API endpoint and pass it to the Navbar component.  As in my previous comment, I cannot use the async function in the root layout to fetch the API response. 

 

Hope this demonstrates my query.

Link to comment
Share on other sites

Hi,

 

Honestly I don't quite get what the actual problem is here. Why you can't use an async function in your layout component? That doesn't make sense. Because it runs on the client?

 

What you're saying directly contradicts this, as far as I can tell:

https://nextjs.org/docs/app/building-your-application/data-fetching#fetching-data-at-the-component-level

 

Also you can use React Query according to this:

https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#use-in-client-components

 

I don't know what's the idea behind Next now a days, but using Next and getting good results building great apps, wasn't this complicated. Honestly I think their just creating more and more convoluted ways to make developers lives as complicated and miserable as possible.

 

Have you run benchmarks in terms of rendering times, UX, KB size and other crazy metrics like lighthouse, logrocket, etc., that clearly demonstrates that using the app router is better than just using pages?

 

3 hours ago, Market South said:

In production, we need to get the dynamic array value from an API endpoint and pass it to the Navbar component.  As in my previous comment, I cannot use the async function in the root layout to fetch the API response. 

If you cannot use async methods to get data from the API, but you must get the data from the API, this is a logical contradiction in the design of your app. Honestly this doesn't make any sense to me.

 

Finally, why not get the data from the API in the navbar component? You actually need that data somewhere else in your app? If you just need it there, just get it there and make the navbar a server component and be done with it, unless your app and UX becomes extremely unusable because of changing that particular component to be a server component. You can also use a React Context instance (unless that is now forbidden in the app router approach) to create your own fetching method or create a custom hook. You can still create a custom hook in Next without being banished from front end development, right?

 

My last advice: Use Vue and Nuxt or Svelte and SvelteKit. Your life will be simpler and happier :D 

 

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