next.js
next.js copied to clipboard
Scroll restoration happens too early before the page gets rendered after hitting browser back button
- [x] I have searched the issues of this repository and believe that this is not a duplicate.
After transiting from one page to another page via next <Link />
, if user clicks the back button and the previous page has getInitialProps
method that takes some time to finish, the scroll position of the previous will be restored before the previous pages gets rendered.
Demo
source code can be found here
After clicking back button, the "go back" text should still be visible (not scroll down to previous position) until the previous page gets rendered
This seems to happen because next's router (and <Link />
) uses window.history.pushState
and then window.history.back
restores the scroll. This doesn't happen with common <a />
because it doesn't use pushState
.
This is an interesting article about how scroll restoration works on each browser. Basically you can disable it and manage the scroll on your own:
history.scrollRestoration = 'manual'
Thanks for you explanation.
Using a plain <a />
tag would cause a full page reload, which is not what I expected. I expect the back button would either not trigger the getInitialProps
method or don’t restore scroll until the getInitialProps
is finished
On 28 Dec 2017, at 12:10, Pablo Varela [email protected] wrote:
This seems to happen because next's router (and <Link />) uses window.history.pushState and then window.history.back restores the scroll. This doesn't happen with common because it doesn't use pushState.
This is an interesting article https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration about how scroll restoration works on each browser. Basically you can disable it and manage the scroll on your own:
history.scrollRestoration = 'manual' — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/zeit/next.js/issues/3303#issuecomment-354225323, or mute the thread https://github.com/notifications/unsubscribe-auth/AAncD7Vrn7rZJW5NYdokbPsXWn7O4N5Eks5tExSmgaJpZM4QiFwt.
Yeah, I understand. I'm not sure what the solution would be from within next.
@liweinan0423 I'm not sure but this could be a workaround
We've come across this too and it feels like something next should be doing internally. Otherwise everyone needs to write their own code to store scrollpositions and then scroll on completion of getInitialProps.
we're having a related issue where, upon clicking a link from a scrolled-down position (say, result 20 in a list) the new page shows up halfway down so the user needs to scroll up to see the item itself.
here's an example screen capture of the issue we're having:
We're also running into this issue and it makes the user experience very frustrating to use. @arunoda Any thoughts or way to fix this?
I was having this same issue as @mgiraldo awhile back and I originally was just using router.push(), but as soon as I switched to using the <Link> tag, my problem resolved itself with out needing to implement a hack like scroll-to-top in component did mount or something ghetto like that. Curious if you are using a Link tag at all?
it is a regular <Link />
see: https://github.com/dpla/dpla-frontend/blob/master/components/ExhibitionsComponents/AllExhibitions/components/ExhibitionsList/index.js#L8-L18
but we ended up using componentDidMount
with window.scrollTo
in the destination page: https://github.com/dpla/dpla-frontend/blob/master/pages/exhibitions/exhibition/index.js#L25-L27
feels very hacky, though... and we have the Back button issue mentioned in the issue in other parts of the site.
you can see the problem in action here: https://dp.la/exhibitions (scroll down and click)... the patch will be up tomorrow
you can see the patched version here: https://beta-staging.dp.la/exhibitions
@mgiraldo I see a slight flash when using the back button. We're running into the same issue.
@mgiraldo In another part of my site I forgot I did this:
Router.push("/account")
window.scrollTo(0, 0)
document.body.focus()
which works when navigating TO a page, but like you said when you hit the back button, its at the top of the window because I think the window recorded that in its history.... What are you doing to mitigate that back button issue? Its like I can get one to work but not the other.
@spencersmb What do you mean switch to using the tag? What tag is that?
We've a similar issues reported here: https://spectrum.chat/thread/3d65b436-b085-4eef-ad84-0941c473e09d
Next.js doesn't hijack scrolling. But this is a real issue. Here's what's happening. This is because of browser's default scrolling behavior. See: https://github.com/zeit/next.js/issues/3303#issuecomment-354225323
When you go back, browser tries to go to the previous scroll position for better UX. But in the ^^ case, the data is not loaded yet. But the data will be there in a bit with the latency of getInitialProps()
. When the data rendered, it's not the correct position as before.
So, in order fix this you can do a few things:
- Cache the data locally and render the page from that cache. You can even update the data in behind the scene. In this case data will be there as the user hit the back button.
- Control scrolling manually via
Router.push()
. (This will return a promise and it'll get resolved when the page is rendered)
We've no plan to manage scrolling inside Next.js yet. But you can do it in the userland and publish a new Link component like LinkBetterScrolling
.
I mean every time a javascript library gets popular and people start using it in production ... some majors issues show up and the official response is: "Fix it yourself." 😢
I fail to understand how does Router.push()
fix scrolling issue that happens when you press button back
in a browser.
@mgiraldo Posted a link to a patched version of his website.
- Go here: https://beta-staging.dp.la/exhibitions
- Scroll to the bottom
- Click on
activism in the US
- Click on
button back
in your browser or just swipe back
There are two major issues:
- Before the list gets rendered. Item detail scrolls to the bottom for no reason.
- When the list gets rendered you are not at the bottom of the list but somewhere in the middle.
I believe it would be nice to at least consider fixing this issue in the future.
Other than that thanks for all the hard work that you put in nextjs. It might take a little longer but we are getting closer to stable libraries in javascript community.
@developer239 We have the exact same issue with our Next.js app as documented here: https://spectrum.chat/thread/3d65b436-b085-4eef-ad84-0941c473e09d
It completely kills the user experience.
Basically, the current page scrolls to the bottom of the page because it's trying to restore scroll position before the previous page is rendered.
@arunoda Could you explain why the Next team isn't considering an official fix to this issue? It seems like it's a breaking bug that completely ruins the user experience.
if i understand correctly the back button/scroll position functionality is a native browser behavior that assumes a standard page refresh (user goes from url x to url y via a standard http request). what client-side javascript-based frameworks do is implement a page transition without making use of the native browser behavior (via pushState
or similar). the browser is counting on itself having a cached version of the page to return you to, but it doesn't (it has an incomplete version that is completed by the framework after the jump is made). however, if your app is not fast enough to produce page transitions/content using the framework (in either direction, backwards or forward), you will get this jarring, second-rate experience.
what i think the nextjs team is saying is the effort of implementing a cross-browser solution to this problem is beyond the scope they are willing to take on.
I think if a next.js team provides its own routing management they should care about similar critical issues as well. For example react-router doesn't have this issue and they don't think that is should be implemented by another developer themselves. Definitely this should be fixed because it makes the user experience very frustrating and confusing.
Hi to all! I think i solve this issue by following the advice of @pablopunk
history.scrollRestoration = 'manual'
using it inside componentDidMount() inside the _app.js file.
However, after some minutes i removed it and the problem seems that has gone.
Strange thing, by using and removing it, it seems that it also fix the issue in a build that is deployed with now. Maybe is only a browser issue?
I came across mentioned issue while building my portfolio site and I think this is really a big breaking thing in user experience overall. I can confirm that problem occurs when the page to go back has more height than the current page. And (IMHO) this has nothing to do with getInitialProps as was stated in official next team response. I get similar bad result with or without using getInitialProps . More likely it happens because scroll restoration occurs before route change but not after it. Try to set simple page transitions and you'll see that scroll height jumps instantly despite the content of current page is still there. In my case I found temporary solution. This involves setting the height of "html" tag to a very high value (like 15000px) on onRouteChangeStart event, and restoring its original height in 2 seconds after onRouteChangeComplete. This works for me and hope it will help someone until we get official fix. Good luck.
My solution for restoring scroll position when the browser back button clicked:
_app.js
componentDidMount() {
const cachedPageHeight = []
const html = document.querySelector('html')
Router.events.on('routeChangeStart', () => {
cachedPageHeight.push(document.documentElement.offsetHeight)
})
Router.events.on('routeChangeComplete', () => {
html.style.height = 'initial'
})
Router.beforePopState(() => {
html.style.height = `${cachedPageHeight.pop()}px`
return true
})
}
@hardwit well done! however it works for me only if I restore height asynchronously like this:
Router.events.on("routeChangeComplete", () => { setTimeout(() => { html.style.height = 'initial' }, 1500); });
My solution for restoring scroll position when the browser back button clicked:
_app.js
componentDidMount() { const cachedPageHeight = [] const html = document.querySelector('html') Router.events.on('routeChangeStart', () => { cachedPageHeight.push(document.documentElement.offsetHeight) }) Router.events.on('routeChangeComplete', () => { html.style.height = 'initial' }) Router.beforePopState(() => { html.style.height = `${cachedPageHeight.pop()}px` return true }) }
Where does Router come from?
This worked for me:
_app.js
componentDidMount() {
window.history.scrollRestoration = "manual";
const cachedScroll = [];
Router.events.on("routeChangeStart", () => {
cachedScroll.push([window.scrollX, window.scrollY]);
});
Router.beforePopState(() => {
const [x, y] = cachedScroll.pop();
setTimeout(() => {
window.scrollTo(x, y);
}, 100);
return true;
});
}
For me, the above didn't work. I'm not sure if it was related to the fixed timeout, but beforePopState
didn't trigger at the scroll at the right time.
What I ended up with was storing a potential scroll and then triggering it in onRouteChangeComplete
.
componentDidMount() {
window.history.scrollRestoration = "manual";
const cachedScrollPositions = [];
let shouldScrollRestore = false;
Router.events.on("routeChangeStart", () => {
cachedScrollPositions.push([window.scrollX, window.scrollY]);
});
Router.events.on("routeChangeComplete", () => {
if (shouldScrollRestore) {
const { x, y } = shouldScrollRestore;
window.scrollTo(x, y);
shouldScrollRestore = false;
}
});
Router.beforePopState(() => {
const [x, y] = cachedScroll.pop();
shouldScrollRestore = { x, y };
return true;
});
}
I found some issues with window.history.scrollRestoration = "manual"
on moving through few pages and then when we returning back and reloading page - it's jumping through the top to previous location.
So i found solution to swithing window.history.scrollRestoration
on different window lifeCycles:
_app.tsx
componentDidMount() {
window.history.scrollRestoration = 'auto';
const cachedScrollPositions: number[][] = [];
let shouldScrollRestore: { x: number, y: number };
Router.events.on('routeChangeStart', () => {
cachedScrollPositions.push([window.scrollX, window.scrollY]);
});
Router.events.on('routeChangeComplete', () => {
if (shouldScrollRestore) {
const { x, y } = shouldScrollRestore;
window.scrollTo(x, y);
shouldScrollRestore = null;
}
window.history.scrollRestoration = 'auto';
});
Router.beforePopState(() => {
if (cachedScrollPositions.length > 0) {
const [x, y] = cachedScrollPositions.pop();
shouldScrollRestore = { x, y };
}
window.history.scrollRestoration = 'manual';
return true;
});
}
I'm curious why people are still trying to manually scroll Next.js pages. @arunoda gave a clear explanation of the problem here: https://github.com/zeit/next.js/issues/3303#issuecomment-376804494
It's because getInitialProps runs even when someone clicks on the browser back button. This means it'll try to grab fresh data. But the browser expects the data to be there already. Just cache any remote data from getInitialProps in the client and the problem should go away. I gave an example here: https://levelup.gitconnected.com/6-tips-using-next-js-for-your-next-web-app-e3f056fa46
I added this code and it works perfect for me
`componentDidMount() {
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual';
const cachedScrollPositions = [];
let shouldScrollRestore;
Router.events.on('routeChangeStart', () => {
cachedScrollPositions.push([window.scrollX, window.scrollY]);
});
Router.events.on('routeChangeComplete', () => {
if (shouldScrollRestore) {
const { x, y } = shouldScrollRestore;
window.scrollTo(x, y);
shouldScrollRestore = false;
}
});
Router.beforePopState(() => {
const [x, y] = cachedScrollPositions.pop();
shouldScrollRestore = { x, y };
return true;
});
}
}`
@pavlo-vasylkivskyi-scx thank you!
I little bit changed your code for using with functional component.
import Router from 'next/router'
let cachedScrollPositions = [];
const Home = props => {
useEffect(() => {
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual';
let shouldScrollRestore;
Router.events.on('routeChangeStart', () => {
cachedScrollPositions.push([window.scrollX, window.scrollY]);
});
Router.events.on('routeChangeComplete', () => {
if (shouldScrollRestore) {
const { x, y } = shouldScrollRestore;
window.scrollTo(x, y);
shouldScrollRestore = false;
}
});
Router.beforePopState(() => {
const [x, y] = cachedScrollPositions.pop();
shouldScrollRestore = { x, y };
return true;
});
}
}, []);
return (...long list of elements with links)
}
Also I cached my list like this https://levelup.gitconnected.com/6-tips-using-next-js-for-your-next-web-app-e3f056fa46. Thank you @jlei523
@pavlo-vasylkivskyi-scx Thank you!
But an error occurred when I refreshed the page and click browser back button.
TypeError: Invalid attempt to destructure none-iterable instance
so I changed your code.
componentDidMount() {
if ("scrollRestoration" in window.history) {
window.history.scrollRestoration = "manual";
const cachedScrollPositions: Array<any> = [];
let shouldScrollRestore;
Router.events.on("routeChangeStart", () => {
if (!shouldScrollRestore) { // <---- Only recording for history push.
cachedScrollPositions.push([window.scrollX, window.scrollY]);
}
});
Router.events.on("routeChangeComplete", () => {
if (shouldScrollRestore) {
const { x, y } = shouldScrollRestore;
window.scrollTo(x, y);
shouldScrollRestore = false;
}
});
Router.beforePopState(() => {
if (cachedScrollPositions.length > 0) { // <---- Add this !
const [x, y] = cachedScrollPositions.pop();
shouldScrollRestore = { x, y };
}
return true;
});
}
}
I've been working for a whole week to find a reliable solution to this problem. I couldn't fine one which:
- works reliably with both Firefox and Chrome.
- works reliably using both the back-button and the forward-button across your website.
- works reliably when you hit the back-/forward-button coming from an external website.
- works reliably even if you hit the refresh button on one of your pages during the session.
- works reliably even if you hammer the back-button or the forward-button multiple times.
I tried various approaches which I found across the web. I tried with different approaches of my own. Not a single one worked reliably. The approach which took me the farthest was one which used scrollRestoration = 'manual'
and sessionStorage
to keep track of the position history. It worked flawlessly except for 3.
.
I'm wondering, has anyone found a solution which works for all items from the list above?
I had this requirement too on an actual project and I came up with the following solution:
https://gist.github.com/schmidsi/2719fef211df9160a43808d505e30b4e
It does not address all points from @feluxe, but it works pretty well for us. Maybe this can help someone as a boilerplate for their own solution.
Here's my take:
https://gist.github.com/claus/992a5596d6532ac91b24abe24e10ae81
It should satisfy all of @feluxe's requirements and behave like native scrollRestoration, although i haven't tested it too much yet. I ran it through Chrome, Firefox and Safari and it worked fine. YMMV if you do out of the ordinary stuff with the router. One caveat is that sometimes scrollTo seems to come a tiny bit too late so you see the top of the page flash for a frame or so.
Caching helped with getInitialProps, since it run client side. Now with getServerSideProps the issue is back.
Caching helped with getInitialProps, since it run client side. Now with getServerSideProps the issue is back.
Yep, when I use getServerSideProps
, scroll restoration happens and after that the page gets rendered.
Im seeing this issue too with getServerSideProps, the scroll restoration happens too early
@Timer
I think we need more notice on this issue, getServerSideProps
would cause scroll restoration happens too early.
The flikering causes very bad user experience, especially on mobile platform.
I came across the same issue today. It is really frustrating that Next.js doesn't have a baked in solution for this problem.
@hardwit's solution works for me.
This is a UX killer that many developers didn't notice. Yes, there should be more notice on this issue for all Next.js users or atleast attach this thread in the common issues so people know the solutions in advance.
Thanks @hardwit for starting all the solutions and it solves for me now.
Also came across this problem, thank you everyone for the possible solutions. This definitely should be officially addressed, huge impact in UX and may even be a necessity for a lot of use cases.
Tested all scripts in this issue and always get problems. I am trying to use this module and it works well for now: https://github.com/moxystudio/next-router-scroll
This behavior should've been fixed on Next.js 10.0.5+.
@Timer tried upgrade to 10.0.5, the problem is still there.
I just tested with 10.0.5 again. Scroll restoration still random, even if it works, the position is not the same as the last position. My pages almost render on the client or render after fetch data on the client.
Has anyone solved this problem? Applying @claus code causes the same problem.
It has already been implemented here. https://demo.vercel.store/ why is it so hard to provide a solution. This moxystudio guy is the best https://github.com/moxystudio/next-router-scroll
It has already been implemented here. https://demo.vercel.store/ why is it so hard to provide a solution. This moxystudio guy is the best https://github.com/moxystudio/next-router-scroll
I tried this package but found some issues, can you share any repo where you might have used this package and it worked?
I used next-router-scroll
on the site: https://animek.fun/
I used
next-router-scroll
on the site: https://animek.fun/
great site, I just bookmarked it lol. can you share the part of code where you used the the package?
I can't share the code but I used use it with disableNextLinkScroll option:
<RouterScrollProvider disableNextLinkScroll={false}>
<Layout>
<Component {...pageProps} />
</Layout>
</RouterScrollProvider>
edit: Got it working by using @hardwit solution
i'm using react-query and forgot to configure staleTime
. In my case the problem was that everytime I clicked on router.back() the data got refetched with a loading state
moxystudio/next-router-scroll
not working anymore.
const [mounted, setMounted] = useState(false)
useEffect(() =>
setMounted(true)
}, [])
return (
renderFullScreenHeight() // scroll restoration was stopped at here
{mounted && renderSomething()}
)
when clicking back on the browser, it won't scroll to the mounted
position.
I fixed it with this code:
const mounted = typeof window !== 'undefined'
-
getServerSideProps
still have the problem.
My latest solution:
let mounted = false
export default function Loading() {
useEffect(() => {
mounted = true
}, [])
return (
{mounted && <div>Run on client only</div>}
)
}
2021-05-15
This is my latest code. it takes me months to figure out, and it solves all other problems:
- Scroll restoration when fetching data on the client.
- https://github.com/vercel/next.js/discussions/17443#discussioncomment-739561
- To avoid re-render pages when the user goes back.
import { useEffect, useRef } from "react"
import { useUpdate } from "react-use"
export default function useMounted() {
const mounted = useRef(false)
const update = useUpdate()
useEffect(() => {
if (mounted.current == false) {
mounted.current = true
update()
}
}, [update])
return mounted.current
}
And in combine with configs:
module.exports = {
experimental: {
scrollRestoration: true
}
}
It looks complicated but solved scroll restoration bugs. 😀
In my case I've fixed it with enabling experimental scrollRestoration
by changing next.config.js
to
module.exports = {
experimental: {
scrollRestoration: true
}
}
It's mentioned also here https://github.com/vercel/next.js/pull/22727#issuecomment-803276977
To set true to scrollRestoration worked well with Safari, but did not work with Chrome (in iOS). :cry:
as @filippofilip95 mentioned, changing the next.config.js
to include the scrollRestoration
expermental flag solved my issue on iOS 14.4.
@claus I'm using your solution in conjunction with a page transition approach (with framer motion), and I'm noticing that exclusively on iOS Chrome, sporadically during history changes that the new scroll position gets applied immediately. It seems like Next's routeChangeComplete
event is firing too early on history changes, my setTimeout
isn't being respected, or something else is going on.
I'm not sure why this is only happening on history changes with iOS Chrome. Has anyone else experienced this?
Here's my take:
https://gist.github.com/claus/992a5596d6532ac91b24abe24e10ae81
It should satisfy all of @feluxe's requirements and behave like native scrollRestoration, although i haven't tested it too much yet. I ran it through Chrome, Firefox and Safari and it worked fine. YMMV if you do out of the ordinary stuff with the router. One caveat is that sometimes scrollTo seems to come a tiny bit too late so you see the top of the page flash for a frame or so.
@ndimatteo In a current project i also ran into something like that (consistently in all browsers though). Something is setting scroll position immediately on click on a Link
, even though i do the {scroll: false}
thing. Weird thing is that this happens only when i'm scrolled down far enough, and it sets the position to something seemingly random (not really random, but also nothing that would make sense to me). I tried to figure out what exactly it is that's setting scroll position but i didn't find anything. Pretty sure it isn't my code (scroll restoration only kicks in much later), and it's not a layout shift. This is driving me nuts.
I'm my project, sometimes I can't even scroll down after changing page in development... if it was a production issue I'd switch framework.
Hi ! It seems to still not work on nextjs.org with Chrome iOS when I click on footer links. I also tried with another projects based on Next 12 and got same issues. Have you any solutions ?
Tested with Chrome IOS (v.96.0.4664.101) on 15.1 and 14.7.
https://user-images.githubusercontent.com/63233026/146800065-a3398c1c-5d80-4ad4-98af-a02844e19629.MP4
Welp, I just ran into this issue 2 days before I'm scheduled to ship my project... None of the solutions work. Nextjs has been awesome, but this bug really sucks. Any chance this has been looked at recently?
Welp, I just ran into this issue 2 days before I'm scheduled to ship my project... None of the solutions work. Nextjs has been awesome, but this bug really sucks. Any chance this has been looked at recently?
@dejesus2010 Trying to get this solved since last month but still didn't get any solution. Any help ?
I'm having the same issue. I've added a setTimeout to delay the scroll, but this is obviously not a sustainable solution.
next: 12.2.2 chrome: 105
module.exports = {
experimental: {
scrollRestoration: true
}
}
this flag triggers replaceState
on every scroll, it broke the browser back/forward buttons for me.
I ended up doing something like this: (in the _app.tsx
)
const router = useRouter();
const scrollCache = useRef<Record<string, [number, number]>>({});
const activeRestorePath = useRef<string>();
useEffect(() => {
if (history.scrollRestoration !== "manual") {
history.scrollRestoration = "manual";
}
const getCurrentPath = () => location.pathname + location.search;
router.beforePopState(() => {
activeRestorePath.current = getCurrentPath();
return true;
});
const onComplete = () => {
const scrollPath = activeRestorePath.current;
if (!scrollPath || !(scrollPath in scrollCache.current)) {
return;
}
activeRestorePath.current = undefined;
const [scrollX, scrollY] = scrollCache.current[scrollPath];
window.scrollTo(scrollX, scrollY);
// sometimes rendering the page can take a bit longer
const delays = [10, 20, 40, 80, 160];
const checkAndScroll = () => {
if (
(window.scrollX === scrollX && window.scrollY === scrollY) ||
scrollPath !== getCurrentPath()
) {
return;
}
window.scrollTo(scrollX, scrollY);
const delay = delays.shift();
if (delay) {
setTimeout(checkAndScroll, delay);
}
};
setTimeout(checkAndScroll, delays.shift());
};
const onScroll = () => {
scrollCache.current[getCurrentPath()] = [window.scrollX, window.scrollY];
};
router.events.on("routeChangeComplete", onComplete);
window.addEventListener("scroll", onScroll);
return () => {
router.events.off("routeChangeComplete", onComplete);
window.removeEventListener("scroll", onScroll);
};
}, []);
hope it helps
next: 12.2.2 chrome: 105
module.exports = { experimental: { scrollRestoration: true } }
this flag triggers
replaceState
on every scroll, it broke the browser back/forward buttons for me. I ended up doing something like this: (in the_app.tsx
)const router = useRouter(); const scrollCache = useRef<Record<string, [number, number]>>({}); const activeRestorePath = useRef<string>(); useEffect(() => { if (history.scrollRestoration !== "manual") { history.scrollRestoration = "manual"; } const getCurrentPath = () => location.pathname + location.search; router.beforePopState(() => { activeRestorePath.current = getCurrentPath(); return true; }); const onComplete = () => { const scrollPath = activeRestorePath.current; if (!scrollPath || !(scrollPath in scrollCache.current)) { return; } activeRestorePath.current = undefined; const [scrollX, scrollY] = scrollCache.current[scrollPath]; window.scrollTo(scrollX, scrollY); // sometimes rendering the page can take a bit longer const delays = [10, 20, 40, 80, 160]; const checkAndScroll = () => { if ( (window.scrollX === scrollX && window.scrollY === scrollY) || scrollPath !== getCurrentPath() ) { return; } window.scrollTo(scrollX, scrollY); const delay = delays.shift(); if (delay) { setTimeout(checkAndScroll, delay); } }; setTimeout(checkAndScroll, delays.shift()); }; const onScroll = () => { scrollCache.current[getCurrentPath()] = [window.scrollX, window.scrollY]; }; router.events.on("routeChangeComplete", onComplete); window.addEventListener("scroll", onScroll); return () => { router.events.off("routeChangeComplete", onComplete); window.removeEventListener("scroll", onScroll); }; }, []);
hope it helps
This does not work for me bro!