The History API (pushState and popstate)

Changes the URL shown in the address bar without a page reload, and handles the browser's back/forward buttons — the mechanism client-side routers are built on.

                history.pushState(state, "", path) / window.addEventListener("popstate", handler)
              

Purpose

history.pushState adds a new entry to the browser’s session history and updates the visible URL, without triggering a page load. popstate fires when the user navigates via the back/forward buttons (or history.back()/forward()), letting application code re-render for the new URL.

Syntax

function navigateTo(path) {
  history.pushState({}, '', path);
  renderPageFor(path);
}

window.addEventListener('popstate', () => {
  renderPageFor(location.pathname);
});

Behavior notes

  • pushState does not trigger navigation, a page load, or a popstate event on its own — the calling code is entirely responsible for rendering whatever the new URL represents.
  • history.replaceState behaves the same way but replaces the current history entry instead of adding a new one — useful for a redirect that shouldn’t itself become a back-button stop.
  • popstate only fires for browser-initiated navigation (back/forward, or a script calling history.back()) — not for a call to pushState itself.
  • The first argument to pushState (a state object) is retrievable later via event.state inside the popstate handler — useful for restoring view-specific data without a re-fetch.

Common mistakes

  • Calling pushState and expecting the page to visually update automatically — nothing renders until the application explicitly does so.
  • Not handling popstate at all, leaving the back button changing the URL with no corresponding visual change.

Runtime notes

Part of the HTML standard — browser-only.

Taught in these lessons

Updated 2026-09-03