HTML onpopstate Attribute

Beginner
⏱️ 7 min read
📚 Updated: Jun 2026
🎯 3 Examples
Events & Handlers

Introduction

The onpopstate attribute is an inline event handler for the popstate event on the window. It runs when the user moves through session history with the Back or Forward button — typically after you have used history.pushState() or history.replaceState() to build client-side routes. Read event.state to know which “page” to show. Put onpopstate on <body> or use window.addEventListener("popstate", …). It does not fire on initial load and does not fire when you call pushState itself.

What You’ll Learn

01

Event handler

Inline JS.

02

popstate

Back / Forward.

03

window / body

Global scope.

04

event.state

Route data.

05

+ pushState

SPA routing.

06

vs hashchange

History API.

Purpose of onpopstate Attribute

The primary purpose of onpopstate is to respond when the active history entry changes — so your app can update the UI when the user clicks Back or Forward instead of reloading from the server. Single-page applications (SPAs) push new URLs with history.pushState() and listen for popstate to swap views.

The popstate event fires on the window object. The handler receives an event whose state property holds the object you passed to pushState or replaceState for the current entry (or null for normal navigations).

💡
Pair with pushState

Call pushState when the user navigates in-app; handle popstate when they use Back/Forward. Together they power History API routing.

📝 Syntax

Set onpopstate on body or assign window.onpopstate in JavaScript:

onpopstate.html
<body onpopstate="handlePopState(event)"></body>

<script>
  window.onpopstate = handlePopState;
  window.addEventListener("popstate", handlePopState);
</script>

Syntax Rules

  • Value is JavaScript executed when the popstate event fires on the window.
  • Valid on <body> as a window event content attribute.
  • Also assignable as window.onpopstate in script.
  • Does not fire when you call pushState or replaceState — only on Back/Forward navigation.
  • Does not fire on the initial page load.
  • Use addEventListener to read event.state reliably.
  • Seed history with replaceState on first load so Back works predictably.

💎 Values

The onpopstate attribute accepts a string of JavaScript code:

  • onpopstate="handlePopState(event)" — Call a named function with the event.
  • onpopstate="renderFromState(event.state)" — Update UI from stored route data.
  • JavaScript: window.onpopstate = (event) => { … } — property assignment.
popstate-handler.html
function handlePopState(event) {
  var route = event.state ? event.state.route : "home";
  document.getElementById("view").textContent = "Showing: " + route;
  document.title = route.charAt(0).toUpperCase() + route.slice(1);
}

window.addEventListener("popstate", handlePopState);

⚡ Quick Reference

Property / APIWhen it runsNotes
popstateBack / Forward in session historyonpopstate
event.stateOn popstateFrom pushState / replaceState
pushState()When you call itDoes not fire popstate
replaceState()When you call itReplaces current entry
hashchangeURL hash (#) changesHash-based routing
Handler attributeonpopstate on bodyWindow event handler

Applicable Elements

TargetSupported?Notes
<body>YesWindow event on body (HTML)
windowYesPrimary target in JavaScript
<div>, <button>NoUse window listeners
<a href> normal clickN/AFull navigation — not popstate
history.back()RelatedTriggers popstate

onpopstate vs hashchange vs onpageshow

APIWhen it firesTypical use
onpopstateSession history Back/ForwardSPA routes with pushState
onhashchangeURL fragment changesHash-based SPA routing
onpageshowPage becomes visiblebfcache restore, refresh data
pushState()When you call itAdd history without reload

Examples Gallery

Inline body handler, dynamic window.onpopstate, and a mini SPA router using event.state.

👀 Live Preview

Push a route, then use your browser Back button — popstate updates the label:

Route: home — click a button, then Back

Example — onpopstate on body

Handle Back/Forward after pushing history entries:

body-onpopstate.html
<body onpopstate="popStateHandler(event)">
  <button onclick="goTo('about')">About</button>
  <p id="status"></p>
</body>

<script>
  history.replaceState({ page: "home" }, "", "/home");
  function goTo(page) {
    history.pushState({ page: page }, "", "/" + page);
  }
  function popStateHandler(event) {
    var page = event.state ? event.state.page : "home";
    document.getElementById("status").textContent =
      "popstate — now on: " + page;
  }
</script>
Try It Yourself

How It Works

pushState adds entries to session history. When the user clicks Back, the browser fires popstate and the body attribute runs popStateHandler(event).

Dynamic Values with JavaScript

Assign the handler on window at runtime:

dynamic-onpopstate.html
<script>
  history.replaceState({ page: "Home" }, "", location.pathname);

  window.onpopstate = function (event) {
    var label = event.state ? event.state.page : "(no state)";
    document.getElementById("log").textContent =
      "popstate — event.state.page: " + label;
  };
</script>
Try It Yourself

How It Works

Assigning window.onpopstate is equivalent to the inline body attribute for the popstate event. Seed the first entry with replaceState so Back has somewhere to go.

Example — Mini SPA with event.state

Intercept link clicks with pushState; restore the view on popstate:

popstate-spa.html
nav.addEventListener("click", function (event) {
  var link = event.target.closest("a[data-route]");
  if (!link) return;
  event.preventDefault();
  var route = link.dataset.route;
  history.pushState({ route: route }, "", "/" + route);
  render(route);
});

window.addEventListener("popstate", function (event) {
  var route = event.state ? event.state.route : "home";
  render(route);
});
Try It Yourself

How It Works

pushState updates the URL without reloading. popstate keeps the UI in sync when the user uses browser history controls — the core pattern behind History API SPAs.

♿ Accessibility

  • Update document title — Change document.title on route change so screen reader users know the page context changed.
  • Move focus on navigation — After rendering a new view, move focus to the main heading or landmark.
  • Announce route changes — Use aria-live="polite" on a status region when content swaps via popstate.
  • Keep real links — Use <a href> with preventDefault + pushState so middle-click and copy-link still work.
  • Do not trap Back — Never block the Back button; popstate should restore prior content naturally.

🧠 How onpopstate Works

1

User navigates in-app

Click, pushState.

pushState
2

User clicks Back

History changes.

Navigate
3

popstate fires

onpopstate runs.

Event
=

Sync UI

Read event.state.

Browser Support

The popstate event, onpopstate handler, and History API (pushState / replaceState) are supported in all modern browsers.

History API · Fully supported

Universal onpopstate support

All major browsers fire popstate when session history changes.

98% Browser support
Google Chrome Fully supported
Full support
Mozilla Firefox Fully supported
Full support
Apple Safari Fully supported
Full support
Microsoft Edge Fully supported
Full support
Internet Explorer IE 10+ supported
Full support
Opera Fully supported
Full support
onpopstate attribute 98% supported

Bottom line: Reliable for History API routing in all modern browsers.

💡 Best Practices

✅ Do

  • Pair pushState with popstate listeners
  • Seed the first entry with replaceState on load
  • Read event.state to restore the correct view
  • Use addEventListener("popstate", …) in production
  • Update document.title on each route change

❌ Don’t

  • Expect popstate when calling pushState
  • Use alert() on every popstate
  • Assume popstate fires on initial page load
  • Rely on popstate for normal full-page link clicks
  • Put onpopstate on random divs — use window

Conclusion

The onpopstate attribute runs JavaScript when the user navigates session history with Back or Forward — essential for single-page apps built on the History API.

Combine it with history.pushState(), read event.state with addEventListener, and seed the initial entry with replaceState so routing behaves predictably from the first visit.

Key Takeaways

Knowledge Unlocked

Five truths every developer should know about onpopstate

Bookmark these before wiring History API routing.

5
Core concepts
🔄 02

+ pushState

Pair both.

Pattern
🔎 03

event.state

Route data.

API
04

Back / Forward

When it fires.

Trigger
05

Not on load

Initial skip.

Gotcha

❓ Frequently Asked Questions

It runs JavaScript when the popstate event fires — when the user navigates session history with Back or Forward after entries were created with the History API.
No. pushState and replaceState do not trigger popstate. Popstate fires later when the user moves through that history.
A copy of the state object you passed to pushState or replaceState for the active entry. It is null for entries from normal full-page navigations.
Call history.pushState() from a button click, then click the browser Back button. Your popstate handler should run and event.state should hold your data.
window.addEventListener("popstate", handler) is preferred in production — you need it to read event.state and can attach multiple listeners.
Yes in all modern browsers with History API support (Chrome, Firefox, Safari, Edge, IE 10+).

Build History API routing

Practice the onpopstate attribute with pushState and SPA examples in the Try It editor.

Try onpopstate demo →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful