Understanding the History API

The History API allows JavaScript to interact with the browser's session history and manage history entries without requiring traditional page navigation for every change.

Webpages can move backward and forward through session history, add new history entries, replace the current entry, associate data with an entry, and respond when the user navigates between history entries.

The History Object

The History API is available through the browser's window.history object. Because window is the global object in normal browser scripts, it is commonly shortened to history.

window.history

history

The object represents the session history associated with the current browser tab or frame and provides methods for navigating and managing its history entries.

History Length

The history.length property reports the number of entries in the session history list for the current browsing context.

console.log(history.length);

The value can include entries created before the current webpage was loaded, so it should not be treated as a count of pages belonging to the current website.

Back and Forward Navigation

The back() method moves to the previous entry in the session history, similar to selecting the browser's Back button.

history.back();

The forward() method moves to the next entry when one is available.

history.forward();

These methods request history navigation. If there is no appropriate entry in that direction, there may be no visible change.

The go() Method

The go() method moves through session history by a specified number of entries relative to the current entry.

history.go(-1);

A negative value moves backward, while a positive value moves forward.

history.go(-2);
history.go(1);

Using history.go(-1) is similar to history.back(), while history.go(1) is similar to history.forward().

Adding History with pushState()

The pushState() method adds a new entry to the browser's session history without loading a new document.

history.pushState({ page: 2 }, "", "?page=2");

The first argument contains state information associated with the new history entry. The second argument is retained for historical API reasons and is normally supplied as an empty string. The third argument specifies the new URL.

After the method runs, the browser can display the new URL while the current document remains loaded.

Replacing History with replaceState()

The replaceState() method modifies the current history entry instead of creating a new one.

history.replaceState({ page: 1 }, "", "?page=1");

This is useful when the current history entry needs updated state information or a different URL but creating an additional Back-button step would not make sense.

The important distinction is that pushState() adds an entry, while replaceState() changes the current entry.

History State Objects

The state value supplied to pushState() or replaceState() can contain information that helps the webpage restore the interface associated with a history entry.

const state = {
  section: "photos",
  page: 3
};

history.pushState(state, "", "?section=photos&page=3");

The current entry's state can be accessed through history.state.

console.log(history.state);

State data should contain the information needed to identify or restore the relevant application state rather than unnecessarily large amounts of data.

Changing the URL

The optional URL supplied to pushState() or replaceState() can update the browser's address bar without loading a new document.

history.pushState({}, "", "/products?page=2");

The new URL must satisfy the API's same-origin restrictions. A webpage cannot use the History API to make its current history entry appear to belong to an unrelated origin.

Changing the URL does not automatically fetch content from that address. JavaScript is responsible for updating the page when the interface needs to reflect the new state.

The popstate Event

The popstate event allows a webpage to respond when the active session history entry changes through history navigation, such as when the user moves backward or forward.

window.addEventListener("popstate", (event) => {
  console.log(event.state);
});

The event's state property contains the state associated with the history entry that became active.

Calling pushState() or replaceState() does not by itself cause a popstate event. The event becomes important when navigating to history entries that have been created or modified by the API.

History Changes vs. Page Navigation

Changing history with pushState() is different from following a normal link to another document. A normal link can request and display another resource, while pushState() changes session history without automatically loading a new document.

This makes the History API useful for interfaces that update part of a webpage while keeping browser Back and Forward navigation meaningful.

The URL and visible content should remain logically connected. If a URL represents a particular view or piece of content, the webpage should be able to display the appropriate state when that URL is visited or restored whenever practical.

Complete History API Example

The following example creates three views. Selecting a button changes the visible content, adds a history entry, and updates the query string without loading another document.

<button type="button" data-page="home">Home</button>
<button type="button" data-page="about">About</button>
<button type="button" data-page="contact">Contact</button>

<h2 id="page-title">Home</h2>
<p id="page-content">This is the home view.</p>

<script>
const title = document.querySelector("#page-title");
const content = document.querySelector("#page-content");
const buttons = document.querySelectorAll("[data-page]");

const pages = {
  home: "This is the home view.",
  about: "This is the about view.",
  contact: "This is the contact view."
};

function showPage(page) {
  title.textContent = page.charAt(0).toUpperCase() + page.slice(1);
  content.textContent = pages[page];
}

buttons.forEach((button) => {
  button.addEventListener("click", () => {
    const page = button.dataset.page;

    showPage(page);
    history.pushState({ page: page }, "", "?page=" + page);
  });
});

window.addEventListener("popstate", (event) => {
  const page = event.state?.page || "home";
  showPage(page);
});
</script>

After selecting several views, the browser's Back and Forward buttons can move between the history entries while JavaScript restores the corresponding content.

Play in Editor

Common History API Mistakes

Mistake Better Approach
Expecting pushState() to load a new page Update the webpage separately when the visible content needs to change.
Using pushState() when no additional history entry is needed Use replaceState() when the current entry should simply be updated.
Changing the URL without updating the interface Keep the displayed content and URL logically synchronized.
Ignoring Back and Forward navigation Handle history navigation and restore the appropriate interface state.
Expecting pushState() to trigger popstate Update the interface directly when pushing state and use popstate for history navigation.
Trying to use an unrelated origin for the new URL Use URLs permitted by the History API's same-origin requirements.

Best Practices

  • Use the History API when changes to an interface should participate meaningfully in browser history.
  • Use pushState() when the user should be able to return to the previous state with the Back button.
  • Use replaceState() when changing the current entry is more appropriate than adding another one.
  • Keep the displayed content, URL, and associated history state synchronized.
  • Handle popstate when the interface needs to respond to Back and Forward navigation.
  • Do not assume that changing a URL with the History API loads content from that URL.
  • Use state objects for useful interface information rather than unnecessarily large amounts of data.
  • Make URLs meaningful and usable whenever a particular interface state can reasonably have its own address.
  • Do not interfere with normal browser navigation unless the application has a clear reason to do so.

Summary

The History API provides access to browser session history through the history object. Methods such as back(), forward(), and go() navigate existing history, while pushState() and replaceState() allow webpages to manage history entries and URLs without automatically loading another document.

The popstate event allows JavaScript to respond when history navigation activates another entry. Next, we will look at the Clipboard API and how webpages can interact with the system clipboard when browser security requirements permit it.