How to restore the scroll position in NextJS?

How to restore the scroll position in NextJS?

Before jumping into this article's central concept of how to restore scroll position in NextJS, let's understand why restoring scroll position is essential or can be essential for any website.

Imagine you are running an e-commerce website with a large collection of products on the home page. A user visits your site, scrolls through the products, and clicks on one to view its details. If they decide to not purchase that item then they will probably go back by clicking the back button. Now, when they hit the back button, you'd want the website to restore to the position they were previously scrolling so they can easily continue browsing from where they left previously. This is important because this enhances the user experience and makes the websites easier for the user to navigate.

As illustrated in the GIF above, when you visit my website and scroll down to read an article, then click the back button, the website automatically restores the position where you left off.

How does it work?

To enable it under the hood, the website must store the user's scrolled position in its session storage when they navigate to a new page. Meaning that, from the above example of an e-commerce site, when a user clicks on a product and is directed to the product details page, the website should store the scrolled position in the browser's session storage. Then, when the user clicks the back button, the website will use the saved scrolled position to restore the page to where the user left off. This process ensures that the user experience remains seamless and uninterrupted.

How to implement scroll position restoration in NextJS?

Now that we understand why restoring scroll position is important and how it works, let's take a look at how to implement this feature in your NextJS application.

The first way to achieve this is fairly easy and just requires a simple change in your next.config.js file. Just adding an scrollRestoration: true to the experimental property should do the job for you.

/** @type {import('next').NextConfig} */
const nextConfig = {
  ... // your other configs
  experimental: {
    scrollRestoration: true,
  },
};

module.exports = nextConfig;

However, as of this writing, this is just an experimental feature of NextJS and might not work always as you want.

Therefore, another approach to achieve this and have full control over scroll restoration in your web application is to use the browser's session storage. To implement this approach, simply update the code on the page where you want to restore the scroll position with the following snippet.

import { useEffect } from 'react';
import { useRouter } from 'next/router';

export default function Home() {
  const router = useRouter();

  // set scroll restoration to manual
  useEffect(() => {
    if ('scrollRestoration' in history && history.scrollRestoration !== 'manual') {
      history.scrollRestoration = 'manual';
    }
  }, []);

  // handle and store scroll position
  useEffect(() => {
    const handleRouteChange = () => {
      sessionStorage.setItem('scrollPosition', window.scrollY.toString());
    };
    router.events.on('routeChangeStart', handleRouteChange);
    return () => {
      router.events.off('routeChangeStart', handleRouteChange);
    };
  }, [router.events]);

  // restore scroll position
  useEffect(() => {
    if ('scrollPosition' in sessionStorage) {
      window.scrollTo(0, Number(sessionStorage.getItem('scrollPosition')));
      sessionStorage.removeItem('scrollPosition');
    }
  }, []);

  return (
    <>
        ...Put your HTML or components for your page here...
    </>
  );
}

Note that all the useEffects hook used in the above code is responsible for the restoration of the scroll position. Now, let's look at what each and every useEffects does.

  // set scroll restoration to manual
  useEffect(() => {
    if ('scrollRestoration' in history && history.scrollRestoration !== 'manual') {
      history.scrollRestoration = 'manual';
    }
  }, []);

The above code changes the scrollRestoration property in the browser's history to manual when the page is loaded. In NextJS, this is set to auto by default. We need to change it to be manaul so that we can control it from our side.

  // handle and store scroll position
  useEffect(() => {
    const handleRouteChange = () => {
      sessionStorage.setItem('scrollPosition', window.scrollY.toString());
    };
    router.events.on('routeChangeStart', handleRouteChange);
    return () => {
      router.events.off('routeChangeStart', handleRouteChange);
    };
  }, [router.events]);

Likewise, in the second useEffect hook, as shown in the above code, we are simply storing the user's scroll position in the browser's session storage as scrollPosition a variable whenever the route is changed or whenever the user navigates to another page. This ensures that the scroll position is saved and can be restored when the user hits the back button.

 // restore scroll position
  useEffect(() => {
    if ('scrollPosition' in sessionStorage) {
      window.scrollTo(0, Number(sessionStorage.getItem('scrollPosition')));
      sessionStorage.removeItem('scrollPosition');
    }
  }, []);

The third and the final hook is responsible for setting the user scroll position to their previous position by using the value from the session storage scrollPosition variable. Once the scroll position is set, we also remove the scrollPosition variable from session storage by doing sessionStorage.removeItem('scrollPosition') . This is because we no longer need to store the variable once the user returns to the previous page, and it will be recreated only if they navigate to another page.

And there you have it. Applying this should now make your NextJS web application have the feature of scroll position restoration. This will greatly enhance the user experience by allowing them to seamlessly navigate between pages while maintaining their scroll position.

Conclusion

In this article, we discussed what is scroll position restoration and why it can be important for your web application. We learned how it is done under the hood using the browser's sessionStorage. Similarly, we discussed the two different ways of achieving it in the NextJS application. While the first approach of just adding the scrollRestoration: true in the config is fairly simple but it is just an experimental feature (as of this writing) and does not give much control, the second approach which involves changing the browser's history scrollRestoration to manual and using useEffect hooks to save and restore the scroll position offers more control and can be a more practical solution.

Whether you are a beginner just trying out NextJS or you are a professional, I am sure this article has helped you understand scroll restoration in NextJS. If you found this information useful, please consider sharing it with friends or colleagues who may also benefit from it. If you have any questions or would like to discuss the topic further, you can reach out to me on Twitter at twitter.com/aabiscodes or LinkedIn at linkedin.com/in/aabis7.