darkroomengineering / lenis

How smooth scroll should be
https://lenis.darkroom.engineering
MIT License
8.44k stars 366 forks source link

Using Lenis with NextJS 13 #170

Closed PixelPage-YT closed 6 months ago

PixelPage-YT commented 1 year ago

Describe the bug

Current Workaround If you have resizing issues, consider this variant: https://github.com/studio-freight/lenis/issues/170#issuecomment-1635443119

Normal variant below:

'use client';

import Lenis from '@studio-freight/lenis';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, useRef } from 'react';

/**
 * Wrapper for Lenis
 */
export default function SmoothScroller() {
    const lenis = useRef<Lenis | null>(null);
    const pathname = usePathname();
    const searchParams = useSearchParams();

    // Scroll to top if the dependencies change (on pathname/searchParams/lenis change)
    useEffect(() => {
        if (lenis.current) lenis.current!.scrollTo(0, { immediate: true });
    }, [pathname, searchParams, lenis]);

    useEffect(() => {
        const lenis = new Lenis();

        lenis.on('scroll', (e: any) => {
            console.log(e);
        });

        function raf(time: number) {
            lenis.raf(time);
            requestAnimationFrame(raf);
        }

        requestAnimationFrame(raf);

        return () => {
            lenis.destroy();
        };
    }, []);

    return <></>;
}

I'm going to leave this open until it's fixed. https://github.com/developit/microbundle/issues/1049 Also needs to be fixed if we don't want to write 'use client'; on the beginning of the lenis wrapper.

clementroche commented 1 year ago

which version of next are you using ? Are you using app directory ?

PixelPage-YT commented 1 year ago

yes, I'm using the app directory (nextjs 13.3.1)

clementroche commented 1 year ago

Can you share your implementation ?

PixelPage-YT commented 1 year ago

Sure:

// adding 'use client'; here would work, but remove the point of next
import { ReactLenis, useLenis } from '@studio-freight/react-lenis';

export default function RootLayout({
      children,
}: {
      children: React.ReactNode;
}) {
        const lenis = useLenis();
        return (
                <html lang="en">
                    <ReactLenis root>{children}</ReactLenis>
                </html>
        )
}

This throws this error Bildschirm­foto 2023-05-05 um 16 48 21

clementroche commented 1 year ago

This is issue is not related to lenis but react-lenis. You're right but i recommend you to not use react-lenis for the moment if you rely on next app directory.

PixelPage-YT commented 1 year ago

yeah, the problem is it doesn't work with normal lenis either (see attachment). Bildschirm­foto 2023-05-06 um 17 22 38

PixelPage-YT commented 1 year ago

I found a solution. You can make a client component and import it from the layout component:

'use client';

import Lenis from '@studio-freight/lenis';

const SmoothScroller = () => {
    const lenis = new Lenis();

    lenis.on('scroll', (e: any) => {
        console.log(e);
    });

    function raf(time: any) {
        lenis.raf(time);
        requestAnimationFrame(raf);
    }

    requestAnimationFrame(raf);
    return <></>;
};

export default SmoothScroller;
rubek-joshi commented 1 year ago

Unfortunately, it seems to work only in the development environment. I am still getting window is not defined errors when building my next app.

PixelPage-YT commented 1 year ago

Unfortunately, it seems to work only in the development environment. I am still getting window is not defined errors when building my next app.

yes, same here. Can't build my app and i am also getting window is not defined errors while in dev mode. It works fine, but there are these errors.

clementroche commented 1 year ago

you should instanciate Lenis inside a useEffect, otherwise it's gonna fail on SSR

'use client';

import Lenis from '@studio-freight/lenis';

const SmoothScroller = () => {
    useEffect(()=>{
      const lenis = new Lenis();

      function raf(time: any) {
        lenis.raf(time);
        requestAnimationFrame(raf);
      }

      raf()

      return () => {
        lenis.destroy()
      }
    }, [])
};

export default SmoothScroller;
PixelPage-YT commented 1 year ago

This fixed the errors mentioned before, but i got new errors: Bildschirm­foto 2023-05-28 um 17 48 12 Bildschirm­foto 2023-05-28 um 17 48 25

I checked whether this was actually caused by the solution you gave, but when i put it outside "useeffect", the hydration errors disappear and the old errors pop up again.

PixelPage-YT commented 1 year ago

Can someone please look into this? I still get this error and I can't push a production build: Warning: Expected server HTML to contain a matching <div> in <a>. And: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

clementroche commented 1 year ago

are you sure it works if you remove lenis ? it seems not related to Lenis

Kyuuari commented 1 year ago

If you’re using Nextjs app directory with lenis, my current workaround is to create a wrapper provider around react-lenis and then use the provider in the layout file,

docs: https://nextjs.org/docs/getting-started/react-essentials#context

/* components/lenis-provider.tsx */

"use client";
import { Lenis as ReactLenis } from "@studio-freight/react-lenis";
import * as React from "react";

export function LenisProvider({
  children,
  options,
  ...props
}: {
  children: React.ReactNode;
  options?: any;
}) {
  return (
    <ReactLenis root {...props}>
      {children}
    </ReactLenis>
  );
}
/* app/layout.tsx */
import "./globals.css";
import { LenisProvider } from "@/components/lenis-provider";

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <LenisProvider
          options={{
            lerp: 0.1,
            wheelMultiplier: 0.8,
            smoothWheel: true,
          }}
        >
          {children}
        </LenisProvider>
      </body>
    </html>
  );
}

Not sure if it solves your problem but I hope it helps you or others wanting to add lenis to their nextjs projects 😄

Kyuuari commented 1 year ago

Oh wait disregard my other post, using ‘useEffect’ with your previous solution works perfectly fine, sorry I should have read comments better:

"use client";
import Lenis from "@studio-freight/lenis";
import { useEffect } from "react";

export const LenisScroller = () => {
  useEffect(() => {
    const lenis = new Lenis();

    lenis.on("scroll", (e: any) => {
       console.log(e);
     });

    function raf(time: number) {
      lenis.raf(time);
      requestAnimationFrame(raf);
    }

    requestAnimationFrame(raf);

    return () => {
      lenis.destroy();
    };
  }, []);

  return <></>;
};

codesandbox: https://codesandbox.io/p/sandbox/old-snowflake-q3x4x5?file=%2Fapp%2Flayout.tsx%3A11%2C3

clementroche commented 1 year ago

I'm waiting for this to be resolved https://github.com/developit/microbundle/issues/1049

jinowac commented 1 year ago

My page does not start from the top on navigating thru links , instead it starts from scroll position of previous page, If I comment lenis it works perfectly fine . This is Next js 13.4.9 app dir . Can some one help? .
I even tried window.scrollTo(0,0) on route change in useEffect , still no use !

jinowac commented 1 year ago

Similiar to this https://stackoverflow.com/questions/75274778/lenis-smooth-scroll-preventing-next-links-default-behaviour-of-scrolling-to-top

PixelPage-YT commented 1 year ago

My page does not start from the top on navigating thru links , instead it starts from scroll position of previous page, If I comment lenis it works perfectly fine . This is Next js 13.4.9 app dir . Can some one help? . I even tried window.scrollTo(0,0) on route change in useEffect , still no use !

Thank you, I also wanted to comment this. This works perfectly for me: https://gist.github.com/PixelPage-YT/adfb2767c8981e9dae585bd31d016e5f but I still have the bug where lenis starts from the previous scroll position.

jinowac commented 1 year ago

@PixelPage-YT Can we notify Lenis team about this ?

PixelPage-YT commented 1 year ago

@PixelPage-YT Can we notify Lenis team about this ?

They'll probably see this

clementroche commented 1 year ago

@PixelPage-YT @jinowac

If you initialize Lenis in _app.js, you have to force reset after every page change by using lenis.scrollTo(0, { immediate: true })

router.events.on('routeChangeStart', () => {
   lenis.scrollTo(0, { immediate: true })
})

But another solution could be to initialize a new Lenis on every page as we did on our starter

jinowac commented 1 year ago

@clementroche gimme solution for Next js 13 app dir . We dont have 'routeChangeStart' event in Next js 13 app dir

jinowac commented 1 year ago

@clementroche @PixelPage-YT Below solution works for me for Next js 13 app dir

const pathname = usePathname(); const searchParams = useSearchParams(); const lenis = useLenis();

useEffect(() => { if (lenis) { lenis.scrollTo(0, { immediate: true }); } }, [pathname, searchParams, lenis]);

Let me know if this ok

jinowac commented 1 year ago

@phile

I'm using this in my nextjs app, lenis resizes weirdly on application state change

"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useLayoutEffect } from "react";
import router from "next/router";

export default function Lenify() {
  useLayoutEffect(() => {
    const lenis = new Lenis();
    const resize = setInterval(() => {
      lenis.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    router.events.on("routeChangeStart", () => {
      lenis.scrollTo(0, { immediate: true });
    });

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.destroy();
    };
  }, []);
  return null;
}

You are wrongly importing "> import router from "next/router"; you shud import { useRouter } from 'next/navigation' in Next js 13 app dir . there is no router.events in Next js 13 app dir

philosofonusus commented 1 year ago

@phile

I'm using this in my nextjs app, lenis resizes weirdly on application state change

"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useLayoutEffect } from "react";
import router from "next/router";

export default function Lenify() {
  useLayoutEffect(() => {
    const lenis = new Lenis();
    const resize = setInterval(() => {
      lenis.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    router.events.on("routeChangeStart", () => {
      lenis.scrollTo(0, { immediate: true });
    });

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.destroy();
    };
  }, []);
  return null;
}

You are wrongly importing "> import router from "next/router"; you shud import { useRouter } from 'next/navigation' in Next js 13 app dir . there is no router.events in Next js 13 app dir

yes, it was for pages dir, I have just wanted to fix this for app dir

philosofonusus commented 1 year ago

so my final variant would be something like this, lenis resizes weirdly on state changes so I use resize interval to make resizes manually, I also use tempus to make optimisation for raf but I think it would be better to put this interval inside of onFrame function within certain amount of frames

"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useEffect, useLayoutEffect, useRef } from "react";
import { usePathname, useSearchParams } from "next/navigation";

export default function Lenify() {
  const lenis = useRef<Lenis | null>(null);
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (lenis.current) lenis.current!.scrollTo(0, { immediate: true });
  }, [pathname, searchParams, lenis]);

  useLayoutEffect(() => {
    lenis.current = new Lenis();

    const resize = setInterval(() => {
      lenis.current!.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.current!.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.current!.destroy();
      lenis.current = null;
    };
  }, []);
  return null;
}
PixelPage-YT commented 1 year ago

Yes, the solutions above work perfectly. I'm just going to leave this open until it's fixed.

patrickdmccarthy commented 1 year ago

Having this issue with next 13 pages directory that I can't solve with any of these fixes.

If the page is scrolling while clicking into a link, my site will keep the position when the new page loads.

I'm trying to run lenis.scrollTo(0, {immediate: true}) during the routeChangeComplete event (which is where my other scroll controls / scroll restoration logic happens), but lenis is always undefined there.

saintjcob commented 11 months ago

My page does not start from the top on navigating thru links , instead it starts from scroll position of previous page, If I comment lenis it works perfectly fine . This is Next js 13.4.9 app dir . Can some one help? . I even tried window.scrollTo(0,0) on route change in useEffect , still no use !

I have the same problem now with Next.js 13 app router, any chance to fix it? It's annoying, but I sooo wanna keep the smooth scroll 😅

btw is there any recommended way of implementing Lenis for Next 13 app router? it works for me (besides scrolling to top on route change), just curious on what's considered the best implementation

philosofonusus commented 11 months ago

Having this issue with next 13 pages directory that I can't solve with any of these fixes.

If the page is scrolling while clicking into a link, my site will keep the position when the new page loads.

I'm trying to run lenis.scrollTo(0, {immediate: true}) during the routeChangeComplete event (which is where my other scroll controls / scroll restoration logic happens), but lenis is always undefined there.

import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useLayoutEffect } from "react";
import router from "next/router";

export default function Lenify() {
  useLayoutEffect(() => {
    const lenis = new Lenis();
    const resize = setInterval(() => {
      lenis.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    router.events.on("routeChangeStart", () => {
      lenis.scrollTo(0, { immediate: true });
    });

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.destroy();
    };
  }, []);
  return null;
}

have you tried putting it as a component and adding somewhere to your _app.tsx? Have just tested it on latest version of next and it does work

philosofonusus commented 11 months ago
"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useEffect, useLayoutEffect, useRef } from "react";
import { usePathname, useSearchParams } from "next/navigation";

export default function Lenify() {
  const lenis = useRef<Lenis | null>(null);
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (lenis.current) lenis.current!.scrollTo(0, { immediate: true });
  }, [pathname, searchParams, lenis]);

  useLayoutEffect(() => {
    lenis.current = new Lenis();

    const resize = setInterval(() => {
      lenis.current!.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.current!.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.current!.destroy();
      lenis.current = null;
    };
  }, []);
  return null;
}

Have you tried it as a component and added it to your root layout?

Kyuuari commented 11 months ago

Yup, the solution above posted by @philosofonusus and @PixelPage-YT should work for the page scroll transitions here's a codesandbox

codesandbox: https://codesandbox.io/p/sandbox/wonderful-field-t35tm8

FettahAud commented 11 months ago

This was helpfull, Thank you people 🫡

FettahAud commented 11 months ago

Any one know how to get a refrence to lenis from any another child component I'm using this solution

Yup, the solution above posted by @philosofonusus and @PixelPage-YT should work for the page scroll transitions here's a codesandbox

codesandbox: https://codesandbox.io/p/sandbox/wonderful-field-t35tm8

I've tried something like this

const lenis = useLenis();

in a child component and it returns undefined

philosofonusus commented 11 months ago

App dir:

"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useContext, useLayoutEffect, useRef } from "react";
import router from "next/router";
import { createContext } from "react";

export const lenisCTX = createContext<Lenis | null>(null);

export const useLenis = () => useContext(lenisCTX);

export default function Lenify({ children }: { children: React.ReactNode }) {
  const lenisRef = useRef<Lenis | null>(null);

  useLayoutEffect(() => {
    const lenis = new Lenis();
    lenisRef.current = lenis;

    const resize = setInterval(() => {
      lenis.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    router.events.on("routeChangeStart", () => {
      lenis.scrollTo(0, { immediate: true });
    });

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenisRef.current = null;
      lenis.destroy();
    };
  }, []);

  return (
    <lenisCTX.Provider value={lenisRef.current}>{children}</lenisCTX.Provider>
  );
}

Pages dir:

import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import {
  createContext,
  useContext,
  useEffect,
  useLayoutEffect,
  useRef,
} from "react";
import { usePathname, useSearchParams } from "next/navigation";

export const lenisCTX = createContext<Lenis | null>(null);

export const useLenis = () => useContext(lenisCTX);

export default function Lenify({ children }: { children: React.ReactNode }) {
  const lenis = useRef<Lenis | null>(null);
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (lenis.current) lenis.current!.scrollTo(0, { immediate: true });
  }, [pathname, searchParams, lenis]);

  useLayoutEffect(() => {
    lenis.current = new Lenis();

    const resize = setInterval(() => {
      lenis.current!.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.current!.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.current!.destroy();
      lenis.current = null;
    };
  }, []);
  return (
    <lenisCTX.Provider value={lenis.current}>{children}</lenisCTX.Provider>
  );
}
philosofonusus commented 11 months ago

App dir:

"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useContext, useLayoutEffect, useRef } from "react";
import router from "next/router";
import { createContext } from "react";

export const lenisCTX = createContext<Lenis | null>(null);

export const useLenis = () => useContext(lenisCTX);

export default function Lenify({ children }: { children: React.ReactNode }) {
  const lenisRef = useRef<Lenis | null>(null);

  useLayoutEffect(() => {
    const lenis = new Lenis();
    lenisRef.current = lenis;

    const resize = setInterval(() => {
      lenis.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    router.events.on("routeChangeStart", () => {
      lenis.scrollTo(0, { immediate: true });
    });

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenisRef.current = null;
      lenis.destroy();
    };
  }, []);

  return (
    <lenisCTX.Provider value={lenisRef.current}>{children}</lenisCTX.Provider>
  );
}

Pages dir:

import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import {
  createContext,
  useContext,
  useEffect,
  useLayoutEffect,
  useRef,
} from "react";
import { usePathname, useSearchParams } from "next/navigation";

export const lenisCTX = createContext<Lenis | null>(null);

export const useLenis = () => useContext(lenisCTX);

export default function Lenify({ children }: { children: React.ReactNode }) {
  const lenis = useRef<Lenis | null>(null);
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (lenis.current) lenis.current!.scrollTo(0, { immediate: true });
  }, [pathname, searchParams, lenis]);

  useLayoutEffect(() => {
    lenis.current = new Lenis();

    const resize = setInterval(() => {
      lenis.current!.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.current!.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.current!.destroy();
      lenis.current = null;
    };
  }, []);
  return (
    <lenisCTX.Provider value={lenis.current}>{children}</lenisCTX.Provider>
  );
}

@FettahAud, use this as a wrapper in your layouts

FettahAud commented 11 months ago

when I use useLenis it returns 'null'

here is how I'm doing

/*child component*/
const lenis = useLenis();

useEffect(() => {
    console.log(lenis);
}, [lenis]);
/*layout component*/
<Lenify>
   {children}
</Lenify>
FettahAud commented 11 months ago

Bro, just last touch and its done, any help

philosofonusus commented 11 months ago

Bro, just last touch and its done, any help

ill look it up, you use pages or app?

FettahAud commented 11 months ago

app

philosofonusus commented 11 months ago

Was my bad, this is updated version for app:

"use client";
import Lenis from "@studio-freight/lenis";
import Tempus from "@studio-freight/tempus";
import router from "next/router";
import { createContext, useContext, useLayoutEffect, useState } from "react";

export const lenisCTX = createContext<Lenis | null>(null);

export const useLenis = () => useContext(lenisCTX);

export default function Lenify({ children }: { children: React.ReactNode }) {
  const [lenis, setLenis] = useState<Lenis | null>(null);

  useLayoutEffect(() => {
    const lenis = new Lenis();

    setLenis(lenis);

    const resize = setInterval(() => {
      lenis.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    router.events.on("routeChangeStart", () => {
      lenis.scrollTo(0, { immediate: true });
    });

    return () => {
      unsubscribe();
      clearInterval(resize);
      setLenis(null);
      lenis.destroy();
    };
  }, []);

  return <lenisCTX.Provider value={lenis}>{children}</lenisCTX.Provider>;
}
tfarhan00 commented 11 months ago

app

You can simply initiate the lenis inside a "use client" root layout.ts then setup lenis instance inside the useEffect() and use document.documentElement as the root element, now you should have a whole page smooth scroll, don't worry about the children getting client-side rendered as long you pass the children like this {children}

FettahAud commented 11 months ago

@philosofonusus Thank you its working. But at first render it returns null then when something happens in the dom it fires up

FettahAud commented 11 months ago

@tfarhan00 This gonna change a lot of stuff

philosofonusus commented 11 months ago

app

You can simply initiate the lenis inside a "use client" root layout.ts then setup lenis instance inside the useEffect() and use document.documentElement as the root element, now you should have a whole page smooth scroll, don't worry about the children getting client-side rendered as long you pass the children like this {children}

so basically you can use this if you don't want to wrap in "use client" component:

"use client";
import Tempus from "@studio-freight/tempus";
import Lenis from "@studio-freight/lenis";
import { useEffect, useLayoutEffect, useRef } from "react";
import { usePathname, useSearchParams } from "next/navigation";

export default function Lenify() {
  const lenis = useRef<Lenis | null>(null);
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (lenis.current) lenis.current!.scrollTo(0, { immediate: true });
  }, [pathname, searchParams, lenis]);

  useLayoutEffect(() => {
    lenis.current = new Lenis();

    const resize = setInterval(() => {
      lenis.current!.resize();
    }, 150);
    function onFrame(time: number) {
      lenis.current!.raf(time);
    }
    const unsubscribe = Tempus.add(onFrame);

    return () => {
      unsubscribe();
      clearInterval(resize);
      lenis.current!.destroy();
      lenis.current = null;
    };
  }, []);
  return null;
}
FettahAud commented 11 months ago

Based on your latest edit, I make it globally usnig zustand, it's much simpler

/*zustand file*/
{
   globalLenis: null,
   setLenis: (data) => set({ globalLenis: data }),
}
/*Lenify*/
useLayoutEffect(() => {
    lenis.current = new Lenis();
    setLenis(lenis.current);
    // ...
}
/*Any component*/
const { globalLenis } = useMyStore()

Thank you for your time

crapthings commented 10 months ago
Module not found: Can't resolve '@studio-freight/lenis'

i've got this with next.js

tried remove .next and rebuild

Caching failed for pack: Error: ENOENT: no such file or directory, rename '/Users/monsterstep/dev/trpc-playground/mockai/.next/cache/webpack/client-development-fallback/0.pack.gz_' -> '/Users/monsterstep/dev/trpc-playground/mockai/.next/cache/webpack/client-development-fallback/0.pack.gz'

FettahAud commented 10 months ago

Is lenis installed? if not write in console npm i @studio-freight/lenis

mohamed-younes16 commented 8 months ago

You can simply add it any client component like a navbar then ininitialize it inside a useEffect()