Automatic scroll restoration in Single Page Applications (SPA)
The ability to restore scroll position is often critical for website usability. It helps users keep the flow of navigation when going back and forth between different pages. Most modern browsers take care of restoring the scroll position automatically, but it doesn’t always work for Single Page Applications where the content is generated on the client’s side, often asynchronously.
Sites like medium and cnbc store the scroll position of each page, and then, when the user navigates back they restore it. The user experience is not ideal, since there’s a clear jump in the scroll position.
We were able to solve this for Chrome by caching the results, so that the results page could be rendered synchronously. However, this didn’t work on Firefox, because this browser tries to restore the scroll position before the page is fully rendered. I made an example to illustrate this scenario: try opening it on chrome and firefox to see the difference.
React-router, the framework we use for routing, once provided scroll restoration out of the box; however, since browsers have started to handle scroll restoration better, the framework maintainers decided to drop support for this and let users implement their own solutions.
We considered several approaches and decided to go with this polyfill, it’s the simplest solution that we found and it seems to work well for our case with no jumps when navigating back. We installed it as a node package, but if you’re looking for a one line solution this might work:
This worked well because we were able to generate the content of the search page synchronously, for websites where this isn’t possible a custom scroll restoration logic will have to be added.
Each site is different, but there’s a chance that the custom scroll restoration logic will start competing with the native browser implementation. This creates a janky behavior where the scroll position will change several times when the user navigates back. After Chrome 46 and Firefox 46, a new modification was introduced to the history api to address this issue: history.scrollRestorationMode. When set to `manual` the user agent will not attempt to restore the scroll position automatically, leaving all responsibility to the site’s custom logic.
Keeping the scroll position consistent when the user navigates back and forth in a SPA can be a hard problem, but not if your pages are generated synchronously and you use this polyfill. As an added bonus, this polyfill keeps the scroll position consistent even if the user leaves your SPA, something that even Chrome's native behavior doesn’t do.