Florian Herlings.
Working with history, pushState and replaceState in Javascript
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.
The Javascript API1 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.
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.
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.
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.2
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 API1 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 SPA3 frameworks to write wonderful things like the 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.
The function itself takes three arguments: a state, a title and an URL:
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:
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.
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.
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.
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.
MDN docs on pushState and replaceState
freeCodeCamp's tutorial on Browser history (8 minute youtube video)
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. ↩ ↩2
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 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. ↩
I do not use any tracking or feedback mechanism on this website. Instead, I would love for you to get in touch with me via twitter.com/__florian to let me know if you liked it or if you have any suggestions or questions.
Made with 💙 in 2021 by Florian Herlings. Contact, Privacy, RSS feed.