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
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.
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.
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
<nav>
<a href="?tab=overview" data-tab="overview">Overview</a>
<a href="?tab=reviews" data-tab="reviews">Reviews</a>
</nav>
<section id="content"></section>
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
| Method | Does |
|---|---|
history.back() | like the Back button |
history.forward() | like Forward |
history.go(-2) | move several steps |
history.length | number of entries |
history.state | the 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.
Newer: Navigation API
pushState is still the widely supported tool.