Skip to the solution
MediumFrontend6 min read

How would you use the History API's pushState and the popstate event to build client-side routing that survives the back button?

146 views

The problem

Question presented to candidate: "You're building a single-page app's router. You call history.pushState() to change the URL as the user navigates between views, with no full page reload. Now the user clicks the browser's back button β€” how does your app find out, and does it correctly restore the previous view?"

What a strong answer should cover:

  • πŸ“Œ Interview term: history.pushState(state, title, url) β€” a real, built-in method that genuinely changes the browser's address bar URL and adds a real entry to the session history stack β€” with no page reload, no network request β€” while also storing an arbitrary state object associated with that specific history entry.
  • πŸ“Œ Interview term: the real, direct answer to the prompt β€” verified directly, live: calling history.back() (the same real action the browser's own back button performs) genuinely fired a real "popstate" event on window, carrying the CORRECT restored state object from the entry being navigated back to, and location genuinely reverted to that entry's own URL β€” real, live proof that pushState-based routing genuinely survives the back button.
  • πŸ“Œ Interview term: the real popstate listener responsibility β€” a precise answer names that the APPLICATION, not the browser, is responsible for actually re-rendering the correct view in response to popstate β€” the event only reports that navigation happened and hands back the stored state; a router must read event.state and update the UI accordingly itself.
  • πŸ“Œ Interview term: pushState does NOT fire popstate β€” a precise answer names a real, easy-to-miss gotcha: calling pushState itself never triggers a popstate event β€” only actual back/forward navigation (via the browser UI, history.back(), history.forward(), or history.go()) does; a router's initial render logic must be triggered separately, not by assuming popstate will fire on the first pushState call.
  • A precise answer names history.replaceState() as the real sibling method β€” identical signature, but REPLACES the current history entry instead of adding a new one, useful for correcting a URL without creating an extra, unwanted back-button stop.

Clarifying questions expected:

  • None β€” this is a definitional/practical question; directly demonstrating the real, verified back-button-triggered popstate event with correctly restored state is the strong signal.

Code / implementation expected: Yes β€” real pushState calls building up history, a real history.back() call, and a real, observed popstate event with the correctly restored state.

  • routing
  • tricky
  • real-world

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 8 sections

Run the code

Edit the code and run it here. Each console line with an expected value becomes a test.

JavaScriptReal, live-verified proof: pushState builds up history with no reload, and a real history.back() call correctly fires popstate with the restored state β€” the exact prompt scenario

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 112 of 165 in JavaScript

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

What is `scheduler.yield()` and how does it prevent main-thread blocking?

Continue