Jul 23, 2025
Improving User Experience on Product Page - Be Careful Messing with History!

This is will be a very quick and brief post about messing with window.history.pushState compared to window.history.replaceState. Both functions have their use case and I want to share a quick reason why you should use one compared to the other and why it matters.
Quick Explanation
As you navigate a website, URL and history management is handled for you but sometimes you may want to update the history stack manually.
As stated directly from its source on mdn web docs, window.history.pushState
does as follows:
The pushState() method of the History interface adds an entry to the browser’s session history stack.
A common use case for wanting to update the history stack manually is to update the URL bar without refreshing the page.
For example, if you are on an online shopfront (ecommerce store) and are viewing products, each product may have a variant.
Let’s imagine here at Sanico Software we are selling leather hats (we aren’t but maybe we should?), the general product URL may be:
Base Product URLhttps://www.sanico.com.au/products/leather-hat
However, we actually offer hats in brown, black and red (this is sounding better and better). Therefore, the individual links of each variant may be:
Brownhttps://www.sanico.com.au/products/leather-hat?variant=1
Blackhttps://www.sanico.com.au/products/leather-hat?variant=2
Redhttps://www.sanico.com.au/products/leather-hat?variant=3
and so on…
It is important for Search Engine Optimisation (SEO) that each variant has it’s own unique URL as it allows bots from search engines (e.g. Google, Bing) to identify it as it’s own page and product. At the end of the day, a variant may be a subset of a base product but it is still essentially it’s own product.
Page Refresh Problem
If we have an interface on the frontend to update the variant (see image below), we need to ensure we update the URL of the entire page. However, we don’t want to refresh the entire page every time a variant is selected - that would lead to very poor user experience for the customer.
Updating the URL is also important for sharing the product and not losing the selected variant. If I wanted to share the black leather hat I found online, I wouldn’t want to share a link that loads in the Brown by default… that would be annoying!
Page Refresh Solution
This is where the pushState()
function comes in handy. If you push a new state onto the history state, it will not refresh the page.
From the backend, you can utilise the fetch()
functionality to retrieve the updated page and cherry pick what data you need to update on the current page.
For example, if you select the brown hat, we may need to update page elements such as the products title, price, image and availability. This provides the tools to update those targeted elements without refreshing the page, wonderful news.
The Problem
If you continue to use pushState()
every time a variant is selected, it adds lots of entries to the history state. If a customer wants to navigate to the previous page, they may hit the back button on their native browser and not want to traverse through 20 variants.
The Alternative Solution
As stated directly from its source on mdn web docs, replaceState()
does as follows:
The replaceState() method of the History interface modifies the current history entry, replacing it with the state object and URL passed in the method parameters. This method is particularly useful when you want to update the state object or URL of the current history entry in response to some user action.
This works very similar to the pushState()
function, however, it does not add to the history stack, it only modifies the current history entry.
The function does not refresh the page, it does not bloat the history stack and it allows a user to navigate to the previous page.
Summary
State management is often handled by the browser itself but can be manually manipulated when required. The pushState()
and replaceState()
functions offer the tools to achieve these manual updates.
The example use case in this post demonstrates a way that replaceState()
can improve user experience when viewing products on an ecommerce store. However, there will be other use cases where pushState()
functionality is desired. Identifying the difference between the two, along with advantages and disadvantages to user experience is important.
As a Software Engineer and service provider, we always ensure any features or updates implemented are not at the detriment of user experience.
Please be aware that this is a simple contrived example to explain the difference between these two specific functions.