Skip to content

JavaScript Tutorial Web APIs

The History API and pushState

Lesson 209 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

The History API lets a page change the URL in the address bar without reloading, and respond when the user presses Back and Forward. Single-page apps use it for navigation, but it is also handy for small things like filters and tabs.

> This API needs a real page URL, so the Try it sandbox cannot run it. The code below is for your own pages.

pushState

script.js
JS
history.pushState({ tab: "reviews" }, "", "?tab=reviews");

Arguments: a state object you can read later, a title (ignored by browsers, use ""), and the new URL. The URL must be on the same origin. The page does not reload, and a new entry is added to the history, so the Back button goes to the previous URL.

replaceState

Same, but it replaces the current entry instead of adding one. Use it when the change should not create a Back step, such as normalising a URL.

script.js
JS
history.replaceState(null, "", "?tab=overview");

popstate: Back and Forward

When the user navigates through history entries (that were added with pushState), the browser fires popstate. Read the state or the URL and update the page.

script.js
JS
window.addEventListener("popstate", event => {
  const tab = new URLSearchParams(location.search).get("tab") ?? "overview";
  showTab(tab);
});

pushState and replaceState do not fire popstate themselves. Only back, forward and history.go() do.

A tabs example

index.html
HTML
<nav>
  <a href="?tab=overview" data-tab="overview">Overview</a>
  <a href="?tab=reviews" data-tab="reviews">Reviews</a>
</nav>
<section id="content"></section>
script.js
JS
function showTab(name) {
  document.querySelector("#content").textContent = "Showing: " + name;
  document.querySelectorAll("nav a").forEach(a => {
    a.setAttribute("aria-current", a.dataset.tab === name ? "page" : "false");
  });
}

document.querySelector("nav").addEventListener("click", event => {
  const link = event.target.closest("a[data-tab]");
  if (!link) return;
  event.preventDefault();
  history.pushState({ tab: link.dataset.tab }, "", link.getAttribute("href"));
  showTab(link.dataset.tab);
});

window.addEventListener("popstate", () => {
  showTab(new URLSearchParams(location.search).get("tab") ?? "overview");
});

showTab(new URLSearchParams(location.search).get("tab") ?? "overview");

The links are real links with real URLs, so the page works without JavaScript (the server can show the right tab), the tab can be shared and bookmarked, and the JavaScript only makes it smoother. That is progressive enhancement.

Other history methods

MethodDoes
history.back()like the Back button
history.forward()like Forward
history.go(-2)move several steps
history.lengthnumber of entries
history.statethe current entry’s state

Cautions

  • Server support: if a user reloads at /products/shoes, the server must be able to answer that URL. Otherwise, use query strings (?tab=...).
  • Do not trap the user: do not push entries on every small change, or Back becomes useless.
  • Scroll and focus: after changing “pages”, manage focus and announce the change for screen reader users (Focus Management).
  • Keep state small and simple: the state object must be cloneable data.
info
Newer: Navigation API
A newer Navigation API is designed to improve on this. Check its browser support before depending on it. pushState is still the widely supported tool.