Question

I'm trying to update the URL using window.location.hash or history.pushState.

What are the differences and advantages of each method?

Was it helpful?

Solution

location.hash has a better support than the history.pushState method.

The advantage of the pushState method is that you can bind a state to the history entry.

If you don't need this state object, I recommend to use the location.hash property, to have better compatibility with older browsers.

location.hash = 'new-hash';
console.log(history.state); // null or undefined

history.pushState({extraData: "some state info"}, '', 'new-hash'); //<---
console.log(history.state); // [object Object] = {"extraData": "some state info"}

OTHER TIPS

Pushstate is the future. It's better because:

  1. It looks cleaner.
  2. On revisit of a deep link you can actually surface real serverside data to support things like SEO and Facebook Open Graph (both send spiders to scrape the html of your page).
  3. Servers don't have access to hash tags data so you don't see it in your server logs so it helps some with analytics.
  4. It helps fix hash tag issues. For example I had an Nginx rewrite to redirect users visiting my app to the same https url. It works in all browsers but Safari which will redirect you to just the domain without the hash (so annoying)!
  5. You can actually use hash tag for what is was meant for, deep linking to sections of long pages.
  6. You can fallback to using real HTTP backend requests for browser that don't support push state OR you can fallback to using hash tag. Both require extra implementation but are easily doable with a little work.

See this talk from Github designer for more: http://warpspire.com/talks/responsive/

history.pushState is better than location.hash. but it is a HTML5 feature. so always better to have a fallback method like below.

if (typeof(window.history.pushState) == 'function') {
    window.history.pushState(null, path, path);
} else {
    window.location.hash = '#!' + path;
}

This is a rather old question (5 years+ at the time of this reply) but a lot of comments on existing replies are asking for an update based on the "current" state of things.

Here's the deal:

HTML5's pushState is supported on all major browsers. If you are supporting older browsers too, history.js provides a great polyfill that lets you use pushState natively and have it easily fall back to legacy URLs for older browsers. Now that pushState is natively supported does not mean, however, that it is definitively the way to go.

There is a very important point that was not raised in any of the older answers and that is that hash URLs and pushState URLs are not only different in the way they appear, but they are actually different in the way they work, too. This fundamental difference between the two might lead you to choose one over the other.

pushState is prettier and cleaner and can be used to completely fake navigation on your site/in your app. For example, GitHub uses it to replace all navigation invisibly. When you click any link on their site, javascript is used to intercept that click and turn it into an AJAX request that updates the contents of the page without loading a new page, while the URL in the location bar changes to match the content that was fetched. This is what pushState was meant to be used for.

In GitHub's case, http://mysite/page1 and http://mysite/page2 are both valid URLs. They are "real" links with "real" content, and initially visiting either page goes through the traditional MVC approach. GitHub uses pushState for navigation in modern browsers, but does not require it - i.e. pushState is used as a "feature add" to (in their opinion) lead to a better user experience when it comes to navigation. When you copy the link in the browser address bar, you are copying a link that was formed via javascript & pushState, but a link that is nevertheless real and valid.

However, if you are starting from scratching and creating a single page app and are not using an MVC framework, chances are that you actually only have a single page, especially if you are not using a dynamic backend (i.e. content is all retrieved via javascript, never generated by a server). In this case, if you use pushState over hash urls, you will need to handle the case that the URL in the browser is not the real URL. I will illustrate.

The user loads your single page app: http://mysite/mainpage

At this point, the browser bar contains the real link to your app, and will take the user to the same view they currently see: the main page. Now they click a link that will take them to a "page" showing the details of some activity. At this point, you want to update the location bar to indicate the change in state. You either use a hash URL and your location bar looks like http://mysite/mainpage#charts/1 or you use pushState and trick it to becoming http://mysite/mainpage/charts/1

If you used pushState, that is not a real link. Navigating via the browser's back/forward buttons will work great and the user will go from the main page to the detail page in both the location bar and in the app (presuming you handle the state change correctly), but if the user bookmarks this link or copies and pastes the link to share it, additional server-side voodoo will be required.

You will need to redirect requests to /mainpage/charts/1 to /mainpage and then use JS to resolve the actual URL and carry out the intended state change operation. A server redirect is absolutely required. You cannot host this page on AWS or on your local disk without a scriptable http server.

Now if you used hash urls, your URL that the user would have seen and interacted with would have been http://mysite/mainpage#/charts/1 and that is a valid, real url. Browsers understand that there is just one page, and whether the user copies and pastes the link or bookmarks it, you just have to handle the hash state in javascript and do not need any server-side magic to make things work.

What no one seems to mention is that pushState and hash links are not mutually exclusive. pushState is just an API to manipulate the browser location that is visible to the user and implement reliable back/forward navigation in modern browsers. It says nothing about what your URL scheme should look like.

In 2017, Google and just about every other JS-capable bot understand hash URLs and traverse them just fine. Google wants you to use the #! abomination for SEO maximization purposes, but even if you don't, your site will be navigable just fine.

The correct answer is to use whatever fits your needs best. If you have real URLs and want to fake navigation between them, use pushState and carry on (optionally with a polyfill for older browsers). But if you have a single-page app, don't pretend not to (unless you have a very good reason). Use hash URLs to make your life easier and not introduce unnecessary problems and then use pushState to manipulate those hash URLs to take advantage of better back/forward support.

I agree with the other answers, but here are a few arguments in favor of location.hash:

  • it works in every browser, including Internet ExploderTM
  • history.pushState is a developing standard, and the API may change in the future
  • if the users opens a link in a new window/tab, a hash-URL makes sure there is no server request needed to load the page (if the correct caching headers are set)
  • The server configuration is easy, since all the server ever sees is the URL without hash-part

edit: I forgot one

  • with hashtags, you can use real links (a href). So you don't have to set up click listeners, which improves performance and reduces code size.

The advantages/disadvantages of window.location.hash vs HTML5 history.pushstate are largely determined by how exactly you want your page to degrade.

Here, you might be interested in graceful degradation in two different scenarios :

The first one being a client which does not have javascript enabled, or an automated bot/crawler visiting your site. This is particularly important from an SEO perspective. If your web-page/web-application uses hash-urls then content that is available through these links is not available to these end-users. This is definitively a problem if you are making sections of content available only via hash-urls with no fallbacks. However it is definitely not a problem if you are using hash-tag links to modify application state which simply don't retain meaning if the page degrades.

As an example consider a scenario where you have a page with three sections of text available in three tabs in a tabbed-layout widget. Now there are two possible scenarios : In the first, you would be loading all the content during the page load - and the tabbed widget will simply serve to hide other sections and show a particular section. So if you use hash-urls in the links you use to construct the tab thumbs, you are using them only to change the client-side application state. When javascript is turned off/is not available, simply the javascript used to construct the tabbed layout does not run, and all the content is immediately available - a reasonable graceful fallback. The different application states simply don't exist in this case and so the hash-urls degrade back to just pointing to anchors in html - the intended purpose. Instead of hash-urls if you were to use html5 pushstate in this case, it would be a bad idea. The reason if that a user might bookmark the link to a particular tab. Because your server would have to take that url and present the user with the client side state which he is expecting. This is not good for a thin server architecture where the client-side should take care of its own state management. You can ofcourse ignore that aspect on server side and let the client check the url right at the beginning upon page load and then switch to appropriate application state. But still it is not a good idea because your server side routing system is concerned with the overhead of additional url fragments which it should ignore, where as it should not be bothered about that fragment at all from an aesthetic perspective. This maps exactly to the requirement for which hash-urls were designed and using them is strongly recommended. If instead in the three sections, the text gets loaded dynamically when a particular tab thumb is clicked, then using hash-urls is not a good idea. The reason being the user will simply have no way to access the linked content if javascript is not available. This is particularly bad for SEO. In this scenario if you handle urls (which would in normal scenario be "hijacked" and "ajaxified") on the server side to make content available in general it is excellent both from point of view of end-user experience as well as SEO.

The second scenario is a client who has an antiquated browser which does not support html5 pushstates. While the above points still hold, in addition I would also argue that forcing users with no pushstate with the same level of degradation as no-javascript is not justifiable. A lot of end-users will simply have no idea why they are receiving a degraded version.

I would recommend you not to tag along with dogmatic motivation of using the latest technology always, and decide the best option for your usage scenario.

Currently, pushState is supported by all modern browsers. Therefore pushState is better than location.hash, but it is a HTML5 feature.

So, location.hash is not dead, in fact it will be around for a long, long time.

A good way to use this is with a lib that supports pushState, but also gracefully degrades to using location.hash.
Example - https://github.com/browserstate/history.js

Moreover location.hash will still be useful for jumping to named anchors. pushState will be a tremendous help in building web apps. I look forward to using it.

I personally prefer pushState because it makes nicer looking URLs and I think that is important for user experience.

You can use history.pushState with a hash fallback using the history.js polyfill if you want to use pushState, but don't want to have issues with older browser support.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top