-
-
Save claus/992a5596d6532ac91b24abe24e10ae81 to your computer and use it in GitHub Desktop.
import useScrollRestoration from "utils/hooks/useScrollRestoration"; | |
const App = ({ Component, pageProps, router }) => { | |
useScrollRestoration(router); | |
return <Component {...pageProps} />; | |
}; | |
export default App; |
import { useEffect } from 'react'; | |
import Router from 'next/router'; | |
function saveScrollPos(url) { | |
const scrollPos = { x: window.scrollX, y: window.scrollY }; | |
sessionStorage.setItem(url, JSON.stringify(scrollPos)); | |
} | |
function restoreScrollPos(url) { | |
const scrollPos = JSON.parse(sessionStorage.getItem(url)); | |
if (scrollPos) { | |
window.scrollTo(scrollPos.x, scrollPos.y); | |
} | |
} | |
export default function useScrollRestoration(router) { | |
useEffect(() => { | |
if ('scrollRestoration' in window.history) { | |
let shouldScrollRestore = false; | |
window.history.scrollRestoration = 'manual'; | |
restoreScrollPos(router.asPath); | |
const onBeforeUnload = event => { | |
saveScrollPos(router.asPath); | |
delete event['returnValue']; | |
}; | |
const onRouteChangeStart = () => { | |
saveScrollPos(router.asPath); | |
}; | |
const onRouteChangeComplete = url => { | |
if (shouldScrollRestore) { | |
shouldScrollRestore = false; | |
restoreScrollPos(url); | |
} | |
}; | |
window.addEventListener('beforeunload', onBeforeUnload); | |
Router.events.on('routeChangeStart', onRouteChangeStart); | |
Router.events.on('routeChangeComplete', onRouteChangeComplete); | |
Router.beforePopState(() => { | |
shouldScrollRestore = true; | |
return true; | |
}); | |
return () => { | |
window.removeEventListener('beforeunload', onBeforeUnload); | |
Router.events.off('routeChangeStart', onRouteChangeStart); | |
Router.events.off('routeChangeComplete', onRouteChangeComplete); | |
Router.beforePopState(() => true); | |
}; | |
} | |
}, [router]); | |
} |
Thanks for this gist!
I have got an issue with dynamic data.
I have a long list of data. so scrolling happens before the data is fetched completely from API. how can I delay the scrolling and perform it when the UI is rendered?
@alivtar Not sure if it is a good idea to refetch data on going back or even forward. The idea is that this data should be fetched from cache on going back. Otherwise, there is no really a good solution for this.
Thanks for this gist! Save my life!
Just here to say NextJS has an experimental feature scrollRestoration
which seems to work smoothly
In your next.config.js file:
module.exports = {
experimental: {
scrollRestoration: true,
},
};
Thank you, it worked for me!
Thanks for this gist.
I used it as starting point for this NPM package, use-next-navigation-event
.
The package does not contain "scroll" related code but can be used to add a scroll restoration strategy without dealing with next/router
.
The package lets you run arbitrary code on "next.js" ruoter navigation event, so you can use it to create a scroll restoration custom flow logic.
Next router gives this on navigation:
newUrl: "....",
This package add these:
newUrl:"...",
oldUrl: "...",
navigationType: "REGULAR_NAVIGATION" | "BACK_OR_FORWARD",
You can use it for whatever you like, here it is a a copy-pastable scroll restoration that uses this package.
NPM Package: https://www.npmjs.com/package/use-next-navigation-event
Docs: https://www.npmjs.com/package/use-next-navigation-event
Repo: https://github.com/tresorama/use-next-navigation-event
I used it as starting point for this NPM package,
use-next-navigation-event
.
@tresorama Nice!
experimental: { scrollRestoration: true, },
Thanks a lot, this works great for me.
I've experienced a few issues with the suggested solutions, so I've built this library https://www.npmjs.com/package/next-restore-scroll-position which fixes the following.
- Fixes a bug where the position was restored after page refresh, as this was not the desired behavior
- Fixes a bug where the position was not restored because
useEffect
was called multiple times (my repo uses useRef instead of local variableshouldScrollRestore
to solve the issue.useRef
is also better thanuseState
becauseuseRef
does not rerender components after an update) - Removes the scroll position from storage after the position is restored to stop a memory leak
Example
yarn add next-restore-scroll-position
import { useScrollRestoration } from 'next-restore-scroll-position';
function App() { // This needs to be NextJS App Component
const router = useRouter();
useScrollRestoration(router);
}
👍 ✅
this is great, thanks!
It works fine for me
Thank you for this resource
The hook version from @MichalSzorad seems to work fairly nicely.
I'm not sure if there's a possible solution, that would be worth the complexity, but one scenario I don't see addressed is as follows:
- Scroll down page A click on link to page B
- On page B click on a link that goes back to page A
- We land and stay at the top of page A
- Click back button twice, scroll is restored to the scrolled down positions, as it was on first view of page A
- Click forward button twice, scroll is again restored to the scrolled down position, even though we had not originally scrolled this second instance of Page A.
How crazy/possible is it to somehow record the scroll position for each pop state version of a url? If we don't, is it better to reset a saved scroll position after arriving to the page via a regular link again?
Thank you!!
This is very helpful!