skip to content

History API and SPA Routing Mechanics

You will learn the three primitives every client router is built from — pushState, replaceState, popstate — and the server rewrite that keeps deep links working. Interviewers ask you to implement a minimal router or explain why a refresh 404s.

on this pageshow

questions

6

In a vanilla JavaScript router, calling history.pushState({}, '', '/about') updates the address bar but the view never changes and the window's popstate listener never runs. Why does the browser not notify you, and what must the router do instead?

level: middleimportance: must knowfreq 78%

basics

~20 s

history.pushState is deliberately silent: it changes the URL and adds a session-history entry without navigating, without a network request, and without firing popstate or hashchange. Only user traversal fires popstate, so the router must render itself after pushing.

open as a page

On the browser's window object, what exactly makes the popstate event fire versus the hashchange event, and does either fire when a script calls history.pushState()?

level: middleimportance: should knowfreq 44%

basics

~20 s

popstate fires when the user traverses session history — Back, Forward, or history.go() — within the same document. hashchange fires when the URL fragment changes through a navigation, such as clicking an anchor link or assigning location.hash. Neither fires for pushState or replaceState.

open as a page

In a client-side router, when should you call history.replaceState() instead of history.pushState(), and what goes wrong if you always push?

level: middleimportance: should knowfreq 58%

basics

~20 s

pushState adds a session-history entry, replaceState overwrites the current one. Use replaceState for high-frequency URL updates such as filters and for redirects; pushing on every keystroke buries the previous page under dozens of entries and breaks the Back button.

open as a page

By default, what does a browser do to the scroll position when the user presses Back, and what does setting history.scrollRestoration to 'manual' change for a single-page app?

level: seniorimportance: should knowfreq 32%

basics

~20 s

By default the browser stores each history entry's scroll offset and restores it during traversal — too early for a single-page app that renders only after popstate, so the page is still short and the offset is clamped. Setting history.scrollRestoration = 'manual' hands that job to your router.

open as a page

What are the rules for the state object passed to history.pushState() — how is it stored, what can it hold, and why is history.state null when a page first loads?

level: seniorimportance: should knowfreq 38%

basics

~20 s

The state object is structured-cloned and stored with the session history entry, so it survives reloads and session restore but drops functions, DOM nodes and class prototypes. The entry created by the initial page load carries no state, so history.state is null until replaceState seeds it.

open as a page