The History API

Introduction

The History API lets you interact with the browser history, trigger the browser navigation methods and change the address bar content.

It’s especially useful in combination with modern Single Page Applications, on which you never make a server-side request for new pages, but instead the page is always the same: just the internal content changes.

A modern JavaScript application running in the browser that does not interact with the History API, either explicitly or at the framework level, is going to be a poor experience to the user, since the back and forward buttons break.

Also, when navigating the app, the view changes but the address bar does not.

And also the reload button breaks: reloading the page, since there is no deep linking, is going to make the browser show a different page

The History API was introduced in HTML5 and is now supported by all modern browsers. IE supports it since version 10, and if you need to support IE9 and older, use the History.js library.

Access the History API

The History API is available on the window object, so you can call it like this: window.history or simply history, since window is the global object.

The History object

Let’s start with the simplest thing you can do with the History API.

Go back to the previous page:

history.back()

this goes to the previous entry in the session history. You can forward to the next page using

history.forward()

This is exactly just like using the browser back and forward buttons.

go() lets you navigate back or forward multiple levels deep. For example

history.go(-1) //equivalent to history.back()
history.go(-2) //equivalent to calling history.back() twice
history.go(1) //equivalent to history.forward()
history.go(3) //equivalent to calling history.forward() 3 times

To know how many entries there are in the history, you can call

history.length

Add an entry to the history

Using pushState() you can create a new history entry programmatically. You pass 3 parameters.

The first is an object which can contain anything (there is a size limit however, and the object needs to be serializable).

The second parameter is currently unused by major browsers, so you generally pass an empty string.

The third parameter is a URL associated to the new state. Note that the URL needs to belong to the same origin domain of the current URL.

const state = { foo: 'bar' }
history.pushState(state, '', '/foo')

Calling this won’t change the content of the page, and does not cause any browser action like changing window.location would.

Modify history entries

While pushState() lets you add a new state to the history, replaceState() allows you to edit the current history state.

history.pushState({}, '', '/posts')
const state = { post: 'first' }
history.pushState(state, '', '/post/first')
const state = { post: 'second' }
history.replaceState(state, '', '/post/second')

If you now call

history.back()

the browser goes straight to /posts, since /post/first was replaced by /post/second

Access the current history entry state

Accessing the property

history.state

returns the current state object (the first parameter passed to pushState or replaceState).

The onpopstate event

This event is called on window every time the active history state changes, with the current state as the callback parameter:

window.onpopstate = event => {
  console.log(event.state)
}

will log the new state object (the first parameter passed to pushState or replaceState) every time you call history.back(), history.forward() or history.go().