8 ms·
When I get my way reviewing a codebase, I make sure that as much state as possible is saved in a URL, sometimes (though rarely) down to the scroll position. I
by jorl17 1y ago
When I get my way reviewing a codebase, I make sure that as much state as possible is saved in a URL, sometimes (though rarely) down to the scroll position.
I genuinely don't understand why people don't get more upset over hitting refresh on a webpage and ending up in a significantly different place. It's mind-boggling and actually insulting as a user. Or grabbing a URL and sending to another person, only to find out it doesn't make sense.
Developing like this on small teams also tends, in my experience, to lead to better UX, because it makes you much more aware of how much state you're cramming into a view. I'll admit it makes development slower, but I'll take the hit most days.
I've seen some people in this thread comment on how having state in a URL is risky because it then becomes a sort of public API that limits you. While I agree this might be a problem in some scenarios, I think there are many others where that is not the case, as copied URLs tend to be short-lived (bookmarks and "browser history" are an exception), mostly used for refreshing a page (which will later be closed) or for sharing . In the remaining cases, you can always plug in some code to migrate from the old URL to the new URL when loading, which will actually solve the issue if you got there via browser history (won't fix for bookmarks though).
- jraph 1y ago> I make sure that as much state as possible is saved in a URL Do you have advice on how to achieve this (for purely client-side stuff)? - How do you represent the state? (a list of key=value pair after the hash?) - How do you make sure it stays in sync? -- do you parse the hash part in JS to restore some stuff on page load and when the URL changes? - How do you manage previous / next? - How do you manage server-side stuff that can be updated client side? (a checkbox that's by default checked and you uncheck it, for instance)
- linked_list 1y agoThe URL spec already takes care of a lot of this, for example /shopping/shirts?color=blue&size=M&page=3 or /articles/my-article-title#preface
- MPSimmons 1y agoOne example I think is super interesting is the NWS Radar site, https://radar.weather.gov/ https://radar.weather.gov/ If you go there, that's the URL you get. However, if you do anything with the map, your URL changes to something like https://radar.weather.gov/?settings=v1_eyJhZ2VuZGEiOnsiaWQiOm51bGwsImNlbnRlciI6Wy0xMTUuOTI1LDM2LjAwNl0sImxvY2F0aW9uIjpudWxsLCJ6b29tIjo2LjM1MzMzMzMzMzMzMzMzMzV9LCJhbmltYXRpbmciOmZhbHNlLCJiYXNlIjoic3RhbmRhcmQiLCJhcnRjYyI6ZmFsc2UsImNvdW50eSI6ZmFsc2UsImN3YSI6ZmFsc2UsInJmYyI6ZmFsc2UsInN0YXRlIjpmYWxzZSwibWVudSI6dHJ1ZSwic2hvcnRGdXNlZE9ubHkiOmZhbHNlLCJvcGFjaXR5Ijp7ImFsZXJ0cyI6MC44LCJsb2NhbCI6MC42LCJsb2NhbFN0YXRpb25zIjowLjgsIm5hdGlvbmFsIjowLjZ9fQ%3D%3D https://radar.weather.gov/?settings=v1_eyJhZ2VuZGEiOnsiaWQiO... Which, if you take the base64 encoded string, strip off the control characters, pad it out to a valid base64 string, you get "eyJhZ2VuZGEiOnsiaWQiOm51bGwsImNlbnRlciI6Wy0xMTUuOTI1LDM2LjAwNl0sImxvY2F0aW9uIjpudWxsLCJ6b29tIjo2LjM1MzMzMzMzMzMzMzMzMzV9LCJhbmltYXRpbmciOmZhbHNlLCJiYXNlIjoic3RhbmRhcmQiLCJhcnRjYyI6ZmFsc2UsImNvdW50eSI6ZmFsc2UsImN3YSI6ZmFsc2UsInJmYyI6ZmFsc2UsInN0YXRlIjpmYWxzZSwibWVudSI6dHJ1ZSwic2hvcnRGdXNlZE9ubHkiOmZhbHNlLCJvcGFjaXR5Ijp7ImFsZXJ0cyI6MC44LCJsb2NhbCI6MC42LCJsb2NhbFN0YXRpb25zIjowLjgsIm5hdGlvbmFsIjowLjZ9fQ==", which decodes into: {"agenda":{"id":null,"center":[-115.925,36.006],"location":null,"zoom":6.3533333333333335},"animating":false,"base":"standard","artcc":false,"county":false,"cwa":false,"rfc":false,"state":false,"menu":true,"shortFusedOnly":false,"opacity":{"alerts":0.8,"local":0.6,"localStations":0.8,"national":0.6}} I only know this because I've spent a ton of time working with the NWS data - I'm founding a company that's working on bringing live local weather news to every community that needs it - https://www.lwnn.news/ https://www.lwnn.news/
- asielen 1y agoIn this case, why encode the string instead of just having the options as plain text parameters?
- qdotme 1y agoNesting, mostly (having used that trick a lot, though I usually sign that record if originating from server). I've almost entirely moved to Rust/WASM for browser logic, and I just use serde crate to produce compact representation of the record, but I've seen protobufs used as well. Otherwise you end up with parsing monsters like ?actions[3].replay__timestamp[0]=0.444 vs {"actions": [,,,{"replay":{"timestamp":[0.444, 0.888]}]}
- yawaramin 1y agoThe OP gives great guidance on these questions.
- thijsvandien 1y agoWhile I like this approach as well, these URLs ending up in the browser history isn’t ideal. Autocomplete when just trying to go to the site causes some undesired state every now and then. Maybe query params offer an advantage over paths here.
- linked_list 1y agoJS does have features for editing the history, but it's a trade-off of not polluting the history too much while still letting the user navigate back and forth
- orphea 1y agoI'm glad to see that prismjs site mentioned by the blog is doing the right thing - when it updates the URL, it replaces the current history item.
- embedding-shape 1y agoDoes that handle back button correctly? Nothing more annoying that sites/apps that overwrites the history incorrectly, so when you press the back button it goes to the entry before you entered the website/app, rather than back into what you were doing in the website/app. Both approaches (appending/rewriting) have their uses, the tricky part is using the right thing for the right action, fuck up either and the experience is abysmal.
- LegionMammal978 1y agoIt's pretty weird, my impression is that the APIs are flexible enough to implement most sane behaviors, but websites keep managing to mess it all up. Perhaps it's just one of those things that no one bothers re-testing as the codebase changes.
- embedding-shape 1y agoIn my experience, the problem is two-fold. First product managers/owners don't consider the URIs, so it ends up not being specified. They say "We should have a page when user clicks X, and then on that page, user can open up modal Y", but none of it is specified in terms of what happens with the URIs and history. Then a developer gets the task to create this, and they too don't push back on what exact URIs are being used, nor how the history is being treated. Either they don't have time, don't have the power to send back tasks to product, simply don't care or just don't think of it. They happily carry along creating whatever URIs make sense to them. No one is responsible for URLs, no one considers that part of UX and design, so no one ends up thinking about it, people implement things as they feel is right, without having a full overview over how things are supposed to fit together. Anyways, that's just based on my experience, I'm sure there are other holes in the process that also exacerbates the issue.
- bgilroy26 1y agoTo save the url length, why not hash all possible states and have the value of the variable in the query string refer to that?
- linked_list 1y agoBecause a hash is by definition a one-way mapping, so then you'd have to keep a map of the reverse mapping hash -> state, which obviously gets impractical with state such as page index or search terms. Better just make two-way "compression" mapping
- yreg 1y agoThey probably have meant something like base64 encode
- linked_list 1y agoIf you base64 encode an ascii string it gets 33% longer
- poncho_romero 1y agoThis is a viable solution, but as the article mentions, you lose intent and readability (e.g. seeing a query parameter for “product=laptop” vs. “state=XBE4eHgU”). And in general, it’s unlikely you’ll run into issues with URL length. Two to eight thousand characters is a lot!
- threetonesun 1y agoI remember bouncing into this limit once in a project because we wanted to make a deeply customized interface shareable without a backend, and while on the site itself we didn't hit a URL limit, when someone shared it via some email clients it added it's own tracking redirect onto the URL which caused it to hit the limit and break.
- 1y ago
- Waterluvian 1y agoThe URL is a public facing interface. If anything goes into the URL, it should already be detailed in the design that the PR’d code is implementing.
- MattDaEskimo 1y agoI can understand "shareable" state (scroll position), but _as much as possible_ seems like overkill. Why not just use localStorage?
- layer8 1y ago> Why not just use localStorage? So that I can operate two windows/tabs of the same site in parallel without them stealing each other’s scroll position. In addition, the second window/tab may have originated from duplicating the first one.
- mejutoco 1y agoYou could work around that if needed with a unique id per tab (I was curious myself) https://stackoverflow.com/questions/11896160/any-way-to-identify-browser-tab-in-javascript https://stackoverflow.com/questions/11896160/any-way-to-iden...
- layer8 1y agoYes, but how do you garbage-collect the stored per-tab state from the local storage? Note that it’s not just per tab, but per history entry of the tab. (When the user goes back, they want the respective state to be restored, and again when going forward in reverse.) Furthermore, with browser features like “reopen closed tab”. Better let the browser manage the state implicitly by managing the URLs.
- MattDaEskimo 1y agoScroll position is _kind of_ fine. Typically I can link the ID in the URL as "state". I was referring to mostly everything else
- phillipseamore 1y agosessionStorage should treat the windows/tabs as separate
- fittom 1y agoI completely agree. In fact, I believe URL design should be part of UX design, and although I've worked with 30+ UX designers, I've never once received guidance on URLs.
- mrexroad 1y agoAs a UX designer that always gives guidance on URL design/strategy, I’ll say it’s not always well received. I’ve run into more than a few engineering or PM teams who feel that’s not w/in scope of design.
- pyrolistical 1y agoAs a dev mentor one of my first lesson is what everybody has in common is design. We all are trying to understand a problem and trying to figure out the best solution. How each role approaches this has some low level specializations but high level learnings can be shared.
- franciscop 1y agoAs a dev who cares about UX, this is crazy to hear but resonates, I've got a few weird looks from people whenever I mentioned some URL improvements. I've also worked with people who understood it. I've seen a correlation though, when people cared enough I could share freely about this, when I did the designer's and dev work I would just add that in (I'm def not a designer, so if I'm doing design work that means the owner doesn't care about design, let alone URLs). I can imagine in your situation as a pure designer how you got it though though, sorry to hear that and I wish other devs cared more. I've def mentoring people to care about it so hope others do so too.
- lenkite 1y agoTo make this work better, URL's should standardize several common semantic query parameters and fragment identifiers (like lines, etc). There is utterly no need for every website to re-invent the wheel here. It would also enable browsers to display long URL's better. It could also reduce the amount of client JS once browsers pick up the job of executing some of the client side interactions on very common fragment changes.
- makeitdouble 1y ago> I genuinely don't understand why people don't get more upset over hitting refresh on a webpage and ending up in a significantly different place. Th web has evolved a lot, as users we're seeing an incredible amount of UX behaviors which makes any single action take different semantics depending on context. When on mobile in particular, there's many cases where going back to the page's initial state is just a PITA the regular way, and refreshing the page is the fastest and cleanest action. Some implementations of infinite scroll won't get you to the content top in any simple way. Some sites are a PITA regarding filtering and ordering, and you're stuck with some of the choices that are inside collapsible blocks you don't even remember where they were. And there's myriads of other situation where you just want the current page in anew and blank state. The more you keep in the url, the more resetting the UX is a chore. Sometimes just refreshing is enough, sometimes cleaning the URL is necessary, sometimes you need to go back to the top and navigate back to the page you were on. And those are situations where the user is already in frustration over some other UX issue, so needing additional efforts just to reset is a adding insult to injury IMHO.
- SoftTalker 1y agoYeah I use a web app regularly for work where they have implemented their own "back" button in the app. The app maintains its own state and history so the browser back button is totally broken. The problem here is that they've implemented an application navigation feature with the same name as a browser navigation feature. As a user, you know you need to click "Back" and your brain has that wired to click the broswer back button. Very annoying. Having "Refresh" break things is (to me) a little more tolerable. I have the mental association of "refresh" as "start over" and so I'm less annoyed when that takes me back to some kind of front page in the app.
- apitman 1y ago> I make sure that as much state as possible is saved in a URL, sometimes (though rarely) down to the scroll position. If your page is server-rendered, you get saved scroll position on refresh for free. One of many ways using JS for everything can subtly break things.
- divan 1y agoAlso reminder that "refresh" is just a code word for "restart (and often redownload) the whole bloody app". It's funny how in web-world people so used to "refreshing" the apps and assume that it's a normal functionality (and not failure mode).
- nextaccountic 1y agoThe web is similar to android, and unlike desktop apps, in that restarting the whole thing is meant to not lose (much) state Actually it would be amazing if desktop applications were like this too, and we had a separate way to go back to the initial screen
- divan 1y agoRestoring state is just one of the features, that can be implemented in any app if needed, with all that baggage that comes with a feature – testing, maintaining, etc. It's just if desktop app becomes so broken/unresponsive, that the only way is to restart it – we consider it a bad experience and bad software. On web "restarting the app" is a normal daily activity when something goes wrong with state/layout/fields/forms, etc.
- nextaccountic 1y agoMost desktops apps are buggy enough to occasionally require restarts or even crash. I don't currently use any program that never crashed on me. On the web "restarting the app" is seamless and not imply anything wrong happened. It's like the Erlang approach to errors, but on steroids The trouble with leaving restoring state to the application do as they wish is that most of times they will get it wrong. Also most of them don't do any of this and will never do. Good defaults matter
- smrtinsert 1y agoUrl state should be descriptive not prescriptive. Either way it is important. Unfortunately my experience on several teams is that businesses never care about stuff like this but users do.
- eru 1y ago> I genuinely don't understand why people don't get more upset over hitting refresh on a webpage and ending up in a significantly different place. It's mind-boggling and actually insulting as a user. Or grabbing a URL and sending to another person, only to find out it doesn't make sense. The two use cases are in slight conflict: most of the time, when I share a URL, I don't want to share a specific scroll position (which probably doesn't even make sense, if the other guy has a different screen size.)
- paulddraper 1y agoScroll, as parent said, is usually not included. Obviously the URL is not all state, it doesn’t save your cursor or IME input. So there is some distinction between “important” and “unimportant” state.
- eru 1y agoPerhaps a better example: should video URLs (like on youtube) include a timestamp or not? Youtube gives you both options, and either can be what you want. Youtube also seems to be smart enough to roughly remember where you were in the video, when you are reloading the page.
- stonecharioteer 1y agoWould this hijack the back button though? Genuinely curious if modifying the URL adds to the location history.
- rossant 1y agoI think you can customize this. You can decide whether each URL changes the location history.
- pbreit 1y agoI would never structure my URLs for performance reasons. 100% for usability.
- DecoySalamander 1y ago> I genuinely don't understand why people don't get more upset over hitting refresh on a webpage and ending up in a significantly different place. I'm in the opposite camp - I find it extremely annoying when sites clutter up the browser history with unnecesarly granular state. E.g. hitting "back" button closes a modal instead of taking me to the previous page.
- lvncelot 1y agoYou can achieve both a clean history and granular state in the URL with using history.replace() and history.push() where necessary.
- tecleandor 1y agoI think that'd be too much. A modal is a subordinate thing to the current window, so I think it shouldn't merit a full url change by itself...
- hk__2 1y agoThis is a completely different issue; you can replace history state in JS without adding new entries.
- latexr 1y ago> I genuinely don't understand why people don't get more upset over hitting refresh on a webpage and ending up in a significantly different place. It's mind-boggling and actually insulting as a user. Or grabbing a URL and sending to another person, only to find out it doesn't make sense. I do dislike those cases. But I also dislike being two-thirds through a video or page, thinking “I’ve got to share this with <friend>, it’s right up their alley”, then hitting my fast combination of keys to share a URL and realising the link shared my exact place, which will make the person think I’m sharing a snippet and not the whole thing, so now I need to send another message to clarify. I like being able to have URLs reproduce a specific state, but I also want that to be a specific decision and not something I can share or save to a bookmark by mistake.
- cassepipe 1y agoI understand the inconvenience to have to leave a keyboard-driven workflow but I think the Share button --> Copy link are common enough now that it shouldn't be an issue. I know firefox also has "Copy clean link" if you right-click on the urlbar. I did not find an extension that does just that but it should be trivial to create one and assign a shortcut to it.
- latexr 1y agoWhenever I try that flow, it either copies the link with the extra details or it screws up the link entirely (e.g. removing the `?v=` from a YouTube link). In other words, it’s extra work for worse results.
- ringer 1y agoExcept when it's not implemented properly and it breaks other workflows. For example, if it only shows a button (not a link or a tag) and copies the link to the clipboard via JavaScript, consider this scenario: I want to send this "link" to my other computer using Firefox's built-in Send Page to Device feature. I have to click Share, click the copy to clipboard button, open a new page, paste the URL, and only then can I share it. If the state were stored in the URL, I could do it in two steps: open context menu -> Send Page to Device, and I'm done.
- zwnow 1y agoI hate sharing links that are like 2 pages long in Whatsapp. Simple as that. If I hit refresh on a page I do it for a reason and I expect to be set at the start of the page. Its no big deal to scroll to where I was. Bloated URLs are a pain to work with too. I highly prefer clean short links. Just store state in local storage and recover it if necessary. If the user has js disabled its kinda their issue state isnt persisted.
- VikingCoder 1y agoI worked at a company that worked hard to make urls do heavy lifting for so many tasks, and it was freaking great.
- _the_inflator 1y ago"hitting refresh" You made my day. I totally agree with you: state, state management, UX/UI. I am extremely proud that I lately implemented exactly this: What if... you pass a link or hit reload - or back button in browser. I have a web app that features a table with a modal preview when hitting a row - boy am I proud to have invested 1 hour in this feature. I like your reasoning: it ain't a technical "because I can dump anything in a url", nope, it is a means to an end, the user experience. Convenience, what ever. I have now a pattern to put in more convenience like this, which should be pretty normal. The only think that remains and bothers me is the verbose URL - the utter mess and clutter in the browser's input field. I feel pain here and there is a conflict inside me between URL aesthetics and flatter the user by providing convenience. I am working on a solution, because this messy URL string hurts my eyes and takes away a little bit the magic and beauty of the state transfer. This abstract mess should be taken care of, also in regard to obfuscation. It ain't cleanly to have full-text strings in the URL, with content which doesn't belong there. But I am on it. I cannot leave the URL string out of the convenience debate, especially not on mobile. Also it can happen that strings get stripped or copy & paste accidentally cut of parts. The shorter the better and as we see, convenience is a brutally hard job to handle. Delicate at so many levels, here error handling due to wrongly formatted strings, a field few people ever entered. My killer feature is the initial page load - it appears way more faster, since there are no skeletons waiting for their fetch request to finish. I am extremely impressed by this little feature and its impact on so many levels. Cheers!
- DanielHB 1y agoFirst SPA I built (without frameworks) I actually wrote my own router that stored most client-side state in the URL as a hash. I remember back then having some problems with IE6 4kb limit on URL length. It actually worked really well, but obviously I had very little state. The only things I didn't store in the hash were form state and raw visualization data (like chart data).