
SPA Feel – Pattern #1 – Restore Scroll Position
While SPAs emerged to address certain limitations of MPAs, they often introduced unnecessary complexity and overhead. Let me explore one pattern for modern MPAs, an SPA-like experience without the drawbacks. By applying this technique, developers can combine the inherent simplicity and performance of MPAs with the smooth user experience of SPAs, making SPAs often outdated.
What's the Issue here?
One of the standout features of SPAs is their ability to maintain state and provide smooth transitions between views. However, MPAs can achieve similar results with minimal JavaScript.
A common issue with MPAs is the loss of scroll position when navigating between pages, applying filters, searches, opening dialogs, or performing other actions that cause a page reload. See the following example:

URL: http://some.js/Number

plus a dialog on top
URL: http://some.js/Number/new
Normally, without any JavaScript help, the scroll position is lost when jumping from the first page to the second page. The browser does not know that the content is the same!
Updating the URL to reflect the current state of the application is a common requirement in SPAs. MPAs do this by just building the right page at a given URL, so it's built in to the web. This means we only need to "remember" the scroll position across pages that "just update the content".
The Pattern #1: Scroll Position Restoration
By storing and restoring the scroll position, you can achieve the same result, no window.history
or history.pushState
mangling required with just a few lines of JavaScript.
// Store scroll position before navigation or filter change
const storeScrollPosition = () => {
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('pagex-scrollPosition', window.scrollY);
});
};
// Restore scroll position after navigation or filter change
const restoreScrollPosition = () => {
const scrollPosition = sessionStorage.getItem('pagex-scrollPosition');
if (scrollPosition) {
window.scrollTo(0, parseInt(scrollPosition, 10));
}
};
restoreScrollPosition();
storeScrollPosition();
This code stores the scroll position in the session storage before the page unloads and restores it when the page loads. The trick here is to call restoreScrollPosition()
right at the end of the HTML content, so it is embedded in the rendering process of the browser. This way, the scroll position is restored before the page has been rendered.
Note: The scrollend
event does not work in Safari (as of March 2025), which is why the beforeunload
event is used to ensure compatibility across different browsers.
Advantages of Enhanced MPAs
Enhanced MPAs remain lightweight, requiring minimal JavaScript and avoiding the overhead of large SPA frameworks. This results in faster load times and reduced resource consumption, benefiting both users and developers.
MPAs push developers to optimize the backend, ensuring fast data retrieval and rendering. This synergy between frontend and backend optimizations leads to a more responsive application overall.
MPAs can fully utilize the browser's optimized rendering capabilities, leading to smoother and more efficient performance. By enhancing MPAs with SPA-like features, developers can provide a seamless user experience without sacrificing performance.
BTW
The back button in the browser does restoring of the scroll position automatically
see history.scrollRestoration
. More about its pros and cons in a later post.