CodePark/ questions / Browser APIs
START PRACTICINGPRACTICE
Frontend question · Browser & web performance › Browser APIs

How does the History API let a single-page app change the URL without reloading?

history.pushState({ id: 42 }, "", "/products/42");
render("/products/42");

window.addEventListener("popstate", (event) => {
  render(location.pathname, event.state);
});

history.pushState changes the URL without a request, and the app draws the new view itself.

How it works: history.pushState(state, "", url) adds a new entry to the session history. It changes the URL in the address bar but makes no request. replaceState does the same, but replaces the current entry instead of adding one. The app then renders the matching view itself. This is what client-side routers do.

Back and forward: when the user presses back or forward, the browser fires popstate with that entry's state. The app re-renders for the new URL. pushState itself does not fire popstate. The new URL must be same-origin.

The server's part: the URLs are real paths, so the server must return the app's HTML for every route. This is a fallback to index.html. Without it, a refresh or a shared link on /products/42 returns a 404.

Routers must also handle scroll restoration, and move focus for accessibility.

  • pushState and replaceState change the URL without a request.
  • popstate fires on back and forward, but not on pushState.
  • The new URL must be same-origin.
  • The server must return the app's HTML for deep links.