Working with history, pushState and replaceState in Javascript
Tags: javascript, historyapi
Published: 2020-04-24 11:43:10

(Originally published on [florianherlings.de](https://florianherlings.de/posts/2020-04-22-history-javascript-pushstate/)).

The browser's history feature is something that we use almost every day, without thinking about it too much. When we navigate from one website to another website, we are creating basically a list of websites that we went to. Whenever we find ourselves in a situation where we want to go back to the previous page (or even a few pages back) we can easily do this with the *back* button in our browser.
![Screenshot of a browser with the back and forward buttons visible.](https://florianherlings.de/public/2020-04-22/browser.png)

The Javascript API[^api] to deal with the browser's history is surprisingly easy to use. On the one hand we can move back and forth through the browser's history and on the other hand we can even manipulate the current and future state.

## Moving back and forward

Imagine our browser's history as a series of elements: There is always one element, that is our "current" element and it represents the page that we are currently seeing.
![History example with three pages: Home, About and Contact](https://florianherlings.de/public/2020-04-22/history_base.png)


Fortunately for us, the functionality behind the *back* and *forward* buttons is provided to us by the browser's `history` object. To go back to the previous page, we can simple call `history.back()` in our Javascript code and the browser will go "one element back" in our history. This way, the previous element will now be marked as the "current" element.
![](https://florianherlings.de/public/2020-04-22/history_back.png)


The browser does not forget about the other elements, so that we can also go forward and make the next element the "current" one by simple calling the `history.forward()` method.

![](https://florianherlings.de/public/2020-04-22/history_forward.png)


We can even go multiple steps at a time (in either direction) using the `history.go()` function. If you provide the value `1` it will go forward one element, while calling `history.go(-2)` will go two elements back.[^go]
![](https://florianherlings.de/public/2020-04-22/history_go.png)

Conveniently, calling `history.go(0)` will not go anywhere, and will instead just reload the page.


## Changing history

Going back and forth between existing entries in our browser's history is exciting and useful. The browser's history API[^api] goes even further and allows us to add new entries or manipulate (to an extend) the entries that already exists. This is a great feature that enables authors of SPA[^spa] frameworks to write wonderful things like the [React router](https://www.npmjs.com/package/react-router) library.

### history.pushState

The browser provides a way for us to add a new entry into the browser's history. Right now all major browser support this feature, but (as of 2020) it is still not 100% where it can be.

Using the browser's `history.pushState` function, we can add a new entry as the "current" entry of the history list. This way we will have added a new entry and at the same time updated what is our current entry at the same time.

![](https://florianherlings.de/public/2020-04-22/history_pushstate.png)

The function itself takes three arguments: a state, a title and an URL:
```javascript
const state = { user: 12 };
const title = 'My new page';
const url   = '/new-page';

history.pushState(state, title, url);
```

This example will add a new entry into our history, with the state of an object carrying the user's id, the new title `My new page` and the URL `/new-page`. The `state` parameter is really meant for those people who write libraries that make good use of it, because the browser itself will not do anything with this data. Unfortunately, the `title` parameter is ignored by modern browsers (as of 2020), but *in theory*  the title of the tab should be updated.

This is why you often see code, uses the `null` value for the first two parameters like so:
```javascript
history.pushState(null, null, '/other-page');
```

The browser will make good use of the last parameter `url`, though: It will update the address bar and show our new URL.
![](https://florianherlings.de/public/2020-04-22/browser_after_state.png)

One thing is interesting, though: It does not reload the content of the page. The browser will not actually go to the provided URL `/new-page`. And this is the wonderful thing about `history.pushState`: It will leave the currently displayed page as is, while updating the browser's address bar with the new URL. It adds a new history entry, without changing what is currently on the page.


As a side note: If I actually wanted to go to the page (meaning: Also load the content of the page), I could easily just call `window.location='/new-page';` to have the browser load that new page.

Because we are manipulating the browser's history, we can still use the *back* button in our browser to move back to the previous URL without any problem.

### history.replaceState

Another way to change your browser's history state is to use the `replaceState` function. It works almost exactly as the `pushState` method mentioned above. The big difference is, that while `pushState` will create a new entry in the browser's history, `replaceState` will onle replace the current state.

![](https://florianherlings.de/public/2020-04-22/history_replacestate.png)

As a side effect of this, using the `replaceState` method will change the URL in the address bar, without creating a new history entry. Even though this was not the use case the developers had in mind, you could use this to change the URL in the address bar without any further side effects.


## Recap

The browser's history is not only a useful feature for actual users, but also for us developers. You can navigate *back* and *forward*, and even *go* multiple steps at a time. The browser's API will also allow you to manipulate the history state by adding a new entry `pushState` or overwrite the current one using `replaceState`. Manipulating the history will not cause the page to reload.

There is a lot more you can do with the history API, but I hope this gave you a good intro.

### Further reading
* MDN docs on [pushState](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) and [replaceState](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)
* freeCodeCamp's [tutorial on Browser history](https://www.youtube.com/watch?v=C9vsQkMu5gk) (8 minute youtube video)


[^api]: API stands for *Application Programming Interface*. The idea behind this is that, an application (in our case the browser) provides an interface for developers. We (developers) can use this interface to do cool things. Often times, a server which provides data via HTTP is also called API, which is not wrong but also does not do it justice.

[^go]: With this in mind, we could even say that `history.back()` is actually the same as `history.go(-1)`. The same is true for `history.forward()`, which is actually the same as `history.go(1)`. 

[^spa]: SPA is an acronym describing the idea of a *Single Page App*. What is meant by this a departure from of the classic approach of having a user navigate from one page to another by making new HTTP calls to the server and receiving a new version of the page. SPAs go a different route in which they only load one page and include all the logic in this single page's Javascript. The Javascript will make intelligent decisions about what to display on the page and will send and load data from a server. There are many frameworks who can help building SPAs, some of which are: React.js, Angular.js and Vue.js.