Closed PixelPage-YT closed 6 months ago
which version of next are you using ? Are you using app directory ?
yes, I'm using the app directory (nextjs 13.3.1)
Can you share your implementation ?
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
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.
yeah, the problem is it doesn't work with normal lenis either (see attachment).
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;
Unfortunately, it seems to work only in the development environment. I am still getting window is not defined errors when building my next app.
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.
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;
This fixed the errors mentioned before, but i got new errors:
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.
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.
are you sure it works if you remove lenis ? it seems not related to Lenis
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 😄
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
I'm waiting for this to be resolved https://github.com/developit/microbundle/issues/1049
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 !
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.
@PixelPage-YT Can we notify Lenis team about this ?
@PixelPage-YT Can we notify Lenis team about this ?
They'll probably see this
@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
@clementroche gimme solution for Next js 13 app dir . We dont have 'routeChangeStart' event in Next js 13 app dir
@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
@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
@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
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;
}
Yes, the solutions above work perfectly. I'm just going to leave this open until it's fixed.
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.
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
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 therouteChangeComplete
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
"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?
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
This was helpfull, Thank you people 🫡
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
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>
);
}
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
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>
Bro, just last touch and its done, any help
Bro, just last touch and its done, any help
ill look it up, you use pages or app?
app
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>;
}
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}
@philosofonusus Thank you its working. But at first render it returns null then when something happens in the dom it fires up
@tfarhan00 This gonna change a lot of stuff
app
You can simply initiate the lenis inside a "use client" root
layout.ts
then setup lenis instance inside theuseEffect()
and usedocument.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;
}
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
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'
Is lenis installed? if not write in console
npm i @studio-freight/lenis
You can simply add it any client component like a navbar then ininitialize it inside a useEffect()
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:
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.