Skip to content

Instantly share code, notes, and snippets.

@claus
Created May 14, 2020 05:35
Show Gist options
  • Star 95 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save claus/992a5596d6532ac91b24abe24e10ae81 to your computer and use it in GitHub Desktop.
Save claus/992a5596d6532ac91b24abe24e10ae81 to your computer and use it in GitHub Desktop.
Restore scroll position after navigating via browser back/forward buttons in Next.js
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]);
}
@PranoySarker
Copy link

It works fine for me
Thank you for this resource

@shawnCaza
Copy link

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:

  1. Scroll down page A click on link to page B
  2. On page B click on a link that goes back to page A
  3. We land and stay at the top of page A
  4. Click back button twice, scroll is restored to the scrolled down positions, as it was on first view of page A
  5. 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?

@Amit-5111998
Copy link

const Feed = ({ navigationList }) => {
const router = useRouter();
useScrollRestoration(router);
}
I have a one-page feed should I send a router like this in the scroll ? @claus @pwfisher

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment