A Single Page Application loads one HTML document and uses JavaScript to dynamically rewrite content and manage navigation via the History API, avoiding full page reloads.
Loads one HTML document and uses JavaScript to dynamically rewrite content on navigation — no full page reloads after the initial load.
The History API (pushState, replaceState, popstate) changes the URL without server requests. Hash routing (#/path) is the legacy alternative.
Fast navigation and rich UX after initial load, but larger JS bundles, SEO challenges, and potential memory leaks in long-running sessions.
SPAs handle navigation client-side for fluid UX; MPAs load fresh HTML per page for better SEO and simpler architecture.
SSR, SSG, and frameworks like Next.js combine SPA-style navigation with server-rendered initial loads, solving the traditional SPA vs MPA tradeoffs.
A Single Page Application (SPA) loads a single HTML page and dynamically updates content as the user navigates, rather than requesting new HTML pages from the server. This architecture powers apps like Gmail, Google Maps, and Spotify Web, delivering fluid, app-like experiences in the browser.
The browser loads one HTML file containing a JavaScript bundle. When the user clicks a link, JavaScript intercepts the click, prevents the default navigation, fetches any needed data via API calls, and updates the DOM — all without a full page reload. The URL changes to reflect the new view, but the page itself never unloads.
SPAs need to update the URL without triggering a server request. Two approaches exist:
history.pushState(state, title, url) to add entries to browser history and history.replaceState() to update the current entry — both change the URL without a server request. The popstate event fires when the user clicks back/forward buttons, allowing the app to restore the previous view. Produces clean URLs like /about but requires server configuration to redirect all paths to index.html.#/about). The browser ignores the hash when making server requests, so refreshing always works without server config. Uses the hashchange event for navigation. Considered legacy due to less clean URLs and weaker SEO.SPA advantages: fast navigation after initial load (no round-trips to the server), rich interactive UX, clean separation between frontend and backend, and potential for offline functionality with service workers. SPA disadvantages: larger initial JavaScript bundle means slower first page load, SEO challenges because the initial HTML is mostly empty, potential memory leaks in long-running sessions from uncleaned event listeners and detached DOM nodes, and the back button can behave unexpectedly if routing isn't implemented carefully.
MPA advantages: each page is a separate HTML document that search engines can crawl easily, smaller per-page JavaScript bundles, and simpler architecture for content-heavy sites. MPA disadvantages: full page reloads on every navigation feel slower, shared state across pages requires cookies or server sessions, and the user experience is less fluid.
Frameworks have evolved to address SPA weaknesses:
SPAs trade initial load time for faster subsequent navigation. The History API (pushState/replaceState + popstate) is the mechanism that makes client-side routing possible. Modern meta-frameworks (Next.js, Nuxt, SvelteKit) have largely solved the SPA vs MPA tradeoff by offering SSR, SSG, and client-side navigation in a single framework.
Fun Fact
Gmail, launched in 2004, was one of the first mainstream SPAs — it used XMLHttpRequest (later known as AJAX) to load emails without page reloads, a technique so novel at the time that the term 'AJAX' wasn't even coined until a year later.