Jump to content
Search Community

GSAP in Nextjs 13 experimental app directory

RobbieC test
Moderator Tag

Recommended Posts

I hope its ok to ask, I'm looking for someone with some experience in the new Nextjs 13 app directory, using gsap; to hopefully help answer a couple questions I have. I'm not 100% sure it's all directly related to the new app dir system or just the fact that they are different client components.

 

So the first question:

In app/layout.js I have a <Header /> client component, in that component I'm using ScrollTrigger to show/hide the navigation bar based on scroll. In app/page.js I'm initializing the ScrollSmoother, so yes my page.js is a client component. In my app/projects/page.js I'm also initializing ScrollSmoother the same way I did in app/page.js. So the SmoothScroller works fine on both pages and during page changes, the issue im running into is that the ScrollTrigger in <Header /> stops working when I change pages. I'm guessing the issue is that on the page change, ScrollTrigger doesn't know what scroller to use because the <Header /> component doesn't change/re-render but the page.js does, almost like it needs to reinitialize when a new page component is rendered.

 

My 2nd question is:

Instead of making my page.js a client component, can/should I make a separate client component that initializes the smoothscroller, something like this:

// components/SmoothScroller.js
'use client'
import { useState } from 'react'
import useIsomorphicLayoutEffect from '../utils/useIsomorphicLayoutEffect'
import { gsap } from 'gsap'
import { ScrollSmoother } from 'gsap/dist/ScrollSmoother'
import { SmootherContext } from '../utils/SmootherContext'

export default function SmoothScroller({ children }) {
  const [smoother, setSmoother] = useState()

  useIsomorphicLayoutEffect(() => {
    gsap.registerPlugin(ScrollSmoother)
    const instance = ScrollSmoother.create({
      smooth: 1,
      normalizeScroll: true,
      ignoreMobileResize: true,
      effects: true,
    })
    setSmoother(instance)
  }, [])

  return (
    <SmootherContext.Provider value={smoother}>
      <div id="smooth-wrapper">
        <div id="smooth-content" className="will-change-transform">
          {children}
        </div>
      </div>
    </SmootherContext.Provider>
  )
}

The issue i've run into with using this SmoothScroller component is that when I use it like this on the page:

// app/projects/page.js
import SmoothScroller from '../../../components/SmoothScroller'
import BlockTitle from '../../../components/BlockTitle'

export default function Projects() {

  return (
    <SmoothScroller>
      <section className="w-full py-[18vh] px overflow-hidden">
        <BlockTitle title="Projects" />
        <div className="container mx-auto">
          <div className="flex">
            <div className="h-screen"></div>
            <div className="h-screen"></div>
          </div>
        </div>
      </section>
    </SmoothScroller>
  )
}

The <BlockTitle> is a client component which is using ScrollTrigger and SplitText, but the scrolltrigger never fires, the markers show up in the correct place and splittext works, its almost like it doesn't know what scroller to attach to.

 

I know I should of created a codesandbox, so I will put one together this evening.

Link to comment
Share on other sites

Yeah, a CodeSandbox or Stackblitz would be pretty critical here. I have ZERO experience with Next.js, so I'm the wrong guy to ask but it kinda sounds like the problem might be that you're not doing proper cleanup. I noticed you're not using gsap.context() nor are you killing the ScrollSmoother anywhere. Typically React requires you to return a cleanup function in your useEffect/useLayoutEffect. I also noticed that you're only importing and registering ScrollSmoother, but ScrollTrigger is a necessary dependency (ScrollSmoother is built on top of ScrollTrigger). 

 

Once we see your minimal demo, I'm sure we'll be in a better position to offer some suggestions. 

Link to comment
Share on other sites

Hi,

 

I'm working on a NextJS using the app folder with ScrollSmoother, so please stand by. But I don't have any experience with the app folder since it's still a beta feature, so please have a bit of patience on the subject.

 

Please stand by.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Sounds good @Rodrigo!

 

@GreenSock - Ill put together a demo, I just through pieces of my code in here, they are actually pretty large components. I won't be able to make an exact duplicate of my projects but ill put something together. But In my project, the only thing not wrapped in the gsap.context() is this peace of code:

    const instance = ScrollSmoother.create({
      smooth: 1,
      normalizeScroll: true,
      ignoreMobileResize: true,
      effects: true,
    })
    setSmoother(instance)

Should that also be inside of gsap.context ?

Link to comment
Share on other sites

Ok so I pieced something together and put it on codesandbox, and after doing so the only issue I really see is that the header animation works on the first initial page that's loaded (so home page or any page you refresh on) but when you switch pages, it does not animate, but the other animations work. I also made a change and wrapped my const instance = ScrollSmoother.create()  in a gsap.context() .

 

The public link is: https://codesandbox.io/p/github/robbiecren07/nextjs13-gsap/draft/solitary-darkness

 

Any help with the head component animation would be great, also any feedback on my code would be greatly appreciated!

 

Oh and Happy Holidays GreenSock fam!

  • Like 1
Link to comment
Share on other sites

Hi,

 

This is a working example for using ScrollSmoother in a NextJS app using the app folder. Basically the approach is to create the ScrollSmoother instance in the main layout component and check the pathname of Next's router to revert and create the ScrollSmoother instance via GSAP Context.

 

https://stackblitz.com/edit/nextjs-ysf649?file=app%2Fimages%2Fpage.js,app%2Fpage.js,app%2Flayout.js

 

If someone gets or finds a better way to approach this, we're all ears. In no way we want to impose a way of doing things but to create a healthy and constructive debate so we all can find the best approach to these issues and features.

 

Happy Tweening!

Link to comment
Share on other sites

@Rodrigo The example you provided does not work, get a bunch of errors in stackblitz.com

 

But I would have to argue that maybe it shouldn't be directly in the `layout.js` since in your example your turning the `layout.js` into a client component, it may be better to create a `providers.js` file and have the scrollsmoother in there. https://beta.nextjs.org/docs/rendering/server-and-client-components#rendering-third-party-context-providers-in-server-components

 

I updated my codesandbox as an example:

https://codesandbox.io/p/github/robbiecren07/nextjs13-gsap/test

  • Like 1
Link to comment
Share on other sites

Hi,

 

Honestly I followed the approach I normally use in NextJS projects. Like I said, since the app folder is still in Beta I haven't payed a lot of attention to it. I tend not to use a lot of time in beta stuff, I rather spend my time on production ready APIs.

 

On that note what you bring makes sense, but the patter being used in the example (which runs completely fine in my local environment, so is just a Stackblitz issue) should be good enough for any set up. That is revert the GSAP Context instance where ScrollSmoother is initialized on every route change, since refreshing is just affecting a ScrollSmoother instance that was created for a completely different DOM structure, so it makes total sense that it doesn't work in the same way when you change the route.

 

Although I'm curious about the layout component being a client one. I thought that the idea of using server components is to reduce bundle size and doing API fetching on the server thus saving requests to the server. On that note the layout component is very small and the amount of kb is quite small. On top of that for using GSAP we need life cycle hooks, which are not available (for logical reasons) on the server components. Finally Next's documentation on the subject is quite slim and not really clear IMHO, which at this stage of their implementation, more likely will contribute to create confusion among users than clarify the benefits of them.

 

I'll look into your suggestions, do some testing and update the examples accordingly.

 

Happy Tweening!

Link to comment
Share on other sites

Yup everything your saying makes sense. I'm also confused on the server/client components and how they work when "stacking" them, etc... i.e. having a client component with a child component that's a server component. I've asked questions as well in their discord but they don't always get answered.

 

On that note, I'm just testing all of this on a rebuild im working on for my portfolio website, which probably won't be production ready for months, as to why I decided to use it to mess around with the beta app dir.

 

I can be the guinea pig for testing gsap in the new app dir 😅

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