0Pricing
HTML Academy · Lesson

The popstate Event

Respond to browser back and forward navigation.

The popstate Event is a free HTML Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the HTML Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What popstate Does

The popstate event fires on the window when the user navigates through the history (back, forward, or a JavaScript history.back call). It lets the SPA re-render the right view in response to browser-initiated navigation.

When It Fires

popstate fires on back/forward button clicks, history.back(), history.forward(), and history.go(n). It does NOT fire when pushState/replaceState are called manually — those are programmatic updates and the page already knows about them.

window.addEventListener("popstate", (e) => {
  console.log("Navigated to", location.pathname, "state:", e.state);
  renderPage(location.pathname);
});

The state Property

The event's state property holds the state object you passed to pushState/replaceState for the entry being navigated to. Use it to restore UI state (open tab, scroll position) that does not live in the URL.

Reading the New URL

The address bar is already updated when popstate fires. Read location.pathname, location.search, location.hash to find the new URL. Combined with state, this is all the information needed to render the destination view.

Initial Page Load

popstate does NOT fire on initial page load — only on navigation. Render the initial view from location at startup, then let popstate handle subsequent navigations. Without this, the initial page is blank after the first back-navigation arrives.

// Initial render
renderPage(location.pathname);
// Future navigation
window.addEventListener("popstate", () => renderPage(location.pathname));

hashchange vs popstate

For pure hash-based routing (URLs like /#/about), the hashchange event fires when the hash changes. popstate also fires for path changes. Modern SPAs use the History API and popstate; hash routing is legacy.

Click Interception

To make a regular <a> click trigger client-side navigation, intercept the click, preventDefault, and call pushState + renderPage. Check for modifier keys (Cmd/Ctrl click for new tab) and pass them through to default behavior.

document.addEventListener("click", (e) => {
  const a = e.target.closest("a");
  if (!a || a.target || e.metaKey || e.ctrlKey) return;
  e.preventDefault();
  history.pushState(null, "", a.href);
  renderPage(location.pathname);
});

Restoring Scroll Position

Browsers attempt to restore scroll position automatically on back navigation, but this is unreliable in SPAs because content is rendered asynchronously. Save scroll in pushState state and restore in popstate: window.scrollTo(0, e.state?.scroll ?? 0).

Async Rendering Caveat

If renderPage fetches data asynchronously, the URL updates before content arrives. Show a loading state, then render. Avoid race conditions where the user clicks back during a fetch and the result of the cancelled fetch overwrites the new view.

Framework Routers

React Router, Vue Router, SvelteKit's router all wrap History + popstate. Even without a framework, the underlying primitive is the same — listen to popstate, intercept links, call pushState. Roll your own router in ~50 lines of vanilla JavaScript when overhead matters.

Common Pitfall: Memory Leaks

Each view's setup may attach event listeners and timers. Without explicit teardown when the view changes (in popstate or in the pushState wrapper), listeners accumulate and degrade performance over time. Track per-view subscriptions with an AbortController and abort on view change.

Knowledge Check

Why does the popstate event NOT fire when you call history.pushState yourself?

Summary

popstate fires on browser back/forward navigation, giving access to the saved state and the new location. It does not fire on pushState/replaceState (your code already knows) or initial page load (render manually). Intercept link clicks for SPA navigation, save scroll position in state, and tear down listeners on view change to prevent leaks.

Frequently asked questions

Is the “The popstate Event” lesson free?

Yes — the full text of “The popstate Event” is free to read here on the web, and the HTML Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the HTML Academy course, upgrade to CoddyKit PRO.

What will I learn in “The popstate Event”?

Respond to browser back and forward navigation. You practise HTML Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start HTML Academy?

No prior experience is required. HTML Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “The popstate Event” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this HTML Academy lesson?

Yes. Every HTML Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. pushState and replaceState
  2. The popstate Event
  3. Hash-Based vs Path-Based Routing
  4. The Navigation API modern browsers
← Back to HTML Academy