Not quite. As the L in URL says, it is the locator or address of the state. The S in REST implies the same, indicating states as the content, not path to it.
When the system evolves, you need to change things. State structure also evolves and you will refactor and rework it. You'll rename things, move fields around.
URL is considered a permanent string. You can break it, but that's a bad thing.
So keeping state in the URL will constrain you from evolving your system. That's bad thing.
I think, that it's more appropriate to treat URL like a protocol. You can encode some state parameters to it and you can decode URL into a state on page load. You probably could even version it, if necessary.
For very simple pages, storing entire state in the URL might work.
To fully describe client side state you also need to look at DOM and cookies. The server can effectively see this stuff too (e.g., during form post).
I design my SSR apps so that as much state as possible lives in the server. I find the session cookie to be far more critical than the URL. I could build most of my apps to be URL agnostic if I really wanted to. The current state of the client (as the server sees it) can determine its logical location in the space of resources. The URL can be more of an optional thing for when we do need to pin down a specific resource for future reference.
Another advantage of not urlizing everything is that you can implement very complex features without a torturous taxonomy. "/workflow/18" is about as detailed as I'd like to get in the URL scheme of a complex back office banking product.
I agree, and this reminds me: I really wish there was better URL (and DNS) literacy amongst the mainstream 'digitally literate'. It would help reduce risk of phishing attacks, allow people to observe and control state meaningful to their experience (e.g. knowing what the '?t=_' does in youtube), trimming of personal info like tracking params (e.g. utm_) before sharing, understanding https/padlock doesn't mean trusted. Etc. Generally, even the most internet-savvy age group, are vastly ill-equipped.
It doesn't help that URLs are badly designed. It's a mix of left- and rightmost significant notation, so the most significant part is in the middle of the URL and hard to spot for someone non-technical.
Really we should be going to com.ycombinator.news/item?id=45789474 instead.
The amount of state that early video games stored in like 256 bytes of ram was actually quite impressive. I bet with some creativity one could do similarly for a web app. Just don’t use gzipped b64-encoded json as your in-url state store!
One of my previous side projects used this idea in the extreme: It's a two-player online word game (scrabble with some twists) but all the state is stored in the URL so it doesn't need a backend.
Yes! This is a very under-utilized concept, especially with client-side execution (WASM etc!)
Few years back, I built a proof-of-concept of a PDF data extraction utility, with the following characteristic - the "recipe" for extracting data from forms (think HIPAA etc) can be developed independently of confidential PDFs, signed by the server, and embedded in the URL on the client-side.
The client can work entirely offline (save the HTML to disk, airgap if you want!) off the "recipe" contained in the URL itself, process the data in WASM, all client-side. It can be trivially audited that the server does not receive any confidential information, but the software is still "web-based", "browser-based" and plays nice with the online IDE - on dummy data.
Found a working demo link - nothing gets sent to the server.
From a human user perspective, HATEOAS is effectively just the web. You follow links to get where you want, and forms let you send data where you want, all traversed from some root entrypoint.
From a machine client perspective, it's a different story. JSON-LD is more-or-less HATEOAS, and it works fine for ActivityPub. It's good when you want to talk to an endpoint that you know what data you want to get from it, but don't necessarily need to know the exact shape or URLs.
When you control both the server and client, HATEOAS extra pain for little to no benefit, especially when it's implemented poorly (ie. when the client still needs to know the exact shape of every endpoint anyway, and HATEOAS really just makes URLs opaque), and it interacts very badly when you need to parse the URL anyway, to pull parts from it or add query parameters.
Hello, I am the author of the article and I can explain a few things.
First of all thank you for your words about the content.
I get why you might feel that way. English isn’t my first language, so I sometimes use GPT to help me polish phrasing or find a smoother rhythm for certain lines.
But the ideas, structure, and all the writing direction are mine. I don’t ask it to write articles for me. It just help me express things more clearly. I treat it more like an editor than a writer.
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).
> 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.
Yeah 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.
Url 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.
> 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.)
> 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.
> 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.
I 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.
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.
First 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).
I really like this approach, and think it should be used more!
In a previous experiment, I created a simple webpage which renders media stored in the URL. This way, it's able to store and render images, audio, and even simple webpages and games. URLs can get quite long, so can store quite a bit of data.
Sure and file names are state & attribute containers too. A URL is a uniform resource locator. You can hack it, of course, but this is no less kludgy than overloading filename. It is never ceases to amaze me seeing the recylcing of good and bad idea in this field.
> Browsers and servers impose practical limits on URL length (usually between 2,000 and 8,000 characters) but the reality is more nuanced. As this detailed Stack Overflow answer explains, limits come from a mix of browser behavior, server configurations, CDNs, and even search engine constraints. If you’re bumping against them, it’s a sign you need to rethink your approach.
So what is the reality? The linked StackOverflow answer claims that, as of 2023, it is "under 2000 characters". How much state can you fit into under 2000 characters without resorting to tricks for reducing the number of characters for different parameters? And what would a rethought approach look like?
1) youre moving state into an arbitrary untrusted easy to modify location.
2) youre allowing users to “deep link” into a page that is deep inside some funnel that may or may not be valid, or even exist at some future point in time, forget skipping the messages/whatever further up.
You probably dont want to do either of those two things.
It's fast becoming a lost art (alongside ensuring the text can be read by the 10% of the male population that is colour blind). It's one thing to coach a junior dev on implementing it properly into a Nextjs app (or whatever is trendy at the time), but quite another to have to explain this stuff to a Product Manager. If you're going to spend copious amounts of time with a designer to make sure the site is pixel perfect visually you should also have time to get your URLs right.
This is a risky idea, actually — at least in its fully expanded form.
Sure, in the prismjs.com case, I have one of those comments in my code too. But I expect it to break one day.
If a site is a content generator and essentially idempotent for a given set of parameters, and you think the developer has a long-term commitment to the URL parameters, then it's a reasonable strategy (and they should probably formalise it).
Perhaps you implement an explicit "save to URL" in that case.
But generally speaking, we eliminated complex variable state from URLs for good reasons to do with state leakage: logged-in or identifying state ending up in search results and forwarded emails, leaking out in referrer logs and all that stuff.
It would be wiser to assume that the complete list of possible ways that user- or session-identifying state in a URL could leak has not yet been written, and to use volatile non-URL-based state until you are sure you're talking about something non-volatile.
Search keywords: obviously. Seach result filters? yeah. Sort direction: probably. Tags? ehh, as soon as you see [] in a URL it's probably bad code: think carefully about how you represent tags. Presentation customisation? No. A backlink? no.
It's also wiser to assume people want to hack on URLs and cut bits out, to reduce them to the bit they actually want to share.
So you should keep truly persistent, identifying aspects in the path, and at least try not to merge trivial/ephemeral state into the path when it can be left in the query string.
One barrier to adoption is that big URLs are just ugly. Things are smooshed together without spaces, URL encoding, human-readable words mixed with random characters, etc. I think even devs who understand what they're looking at find it a little unsatisfying.
Maybe a solution is some kind of browser widget that displays query params in a user-friendly way that hides the ugliness, sort of like an object explorer interface.
Holding the snark aside for second, I think there is some harsh truth here.
Url query params are not popular in the front end developer world for some reason, probably bc the fundamentals of web dev are often skipped in favor of learning leetcode and all the react hooks. Same could be sade for SQL and CSS.
I also don't think its a good look that the author is a CTO and is just discovering how useful url query params are. that being said, its a pretty good and well-written blog post.
This should be used more often. I wish websites like Google could respect the language given in the URL. Always tries to guess what's my language based on IP and fails
91 comments
[ 4.7 ms ] story [ 68.5 ms ] threadURL is considered a permanent string. You can break it, but that's a bad thing.
So keeping state in the URL will constrain you from evolving your system. That's bad thing.
I think, that it's more appropriate to treat URL like a protocol. You can encode some state parameters to it and you can decode URL into a state on page load. You probably could even version it, if necessary.
For very simple pages, storing entire state in the URL might work.
I design my SSR apps so that as much state as possible lives in the server. I find the session cookie to be far more critical than the URL. I could build most of my apps to be URL agnostic if I really wanted to. The current state of the client (as the server sees it) can determine its logical location in the space of resources. The URL can be more of an optional thing for when we do need to pin down a specific resource for future reference.
Another advantage of not urlizing everything is that you can implement very complex features without a torturous taxonomy. "/workflow/18" is about as detailed as I'd like to get in the URL scheme of a complex back office banking product.
Really we should be going to com.ycombinator.news/item?id=45789474 instead.
https://scrobburl.com/ https://github.com/Jcparkyn/scrobburl
Few years back, I built a proof-of-concept of a PDF data extraction utility, with the following characteristic - the "recipe" for extracting data from forms (think HIPAA etc) can be developed independently of confidential PDFs, signed by the server, and embedded in the URL on the client-side.
The client can work entirely offline (save the HTML to disk, airgap if you want!) off the "recipe" contained in the URL itself, process the data in WASM, all client-side. It can be trivially audited that the server does not receive any confidential information, but the software is still "web-based", "browser-based" and plays nice with the online IDE - on dummy data.
Found a working demo link - nothing gets sent to the server.
https://pdfrobots.com/robot/beta/#qNkfQYfYQOTZXShZ5J0Rw5IBgB...
Probably because it sounds like the most poorly named breakfast cereal ever.
From a machine client perspective, it's a different story. JSON-LD is more-or-less HATEOAS, and it works fine for ActivityPub. It's good when you want to talk to an endpoint that you know what data you want to get from it, but don't necessarily need to know the exact shape or URLs.
When you control both the server and client, HATEOAS extra pain for little to no benefit, especially when it's implemented poorly (ie. when the client still needs to know the exact shape of every endpoint anyway, and HATEOAS really just makes URLs opaque), and it interacts very badly when you need to parse the URL anyway, to pull parts from it or add query parameters.
I think of flight stick controllers.
No database. No cookies. No localStorage
Themes chosen. Languages selected. Plugins enabled.
Which have the pattern of rhetoric but no substance. Clearly the author put significant effort it so why get an LLM to add noise?
First of all thank you for your words about the content.
I get why you might feel that way. English isn’t my first language, so I sometimes use GPT to help me polish phrasing or find a smoother rhythm for certain lines.
But the ideas, structure, and all the writing direction are mine. I don’t ask it to write articles for me. It just help me express things more clearly. I treat it more like an editor than a writer.
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).
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.
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.
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.
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.)
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.
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.
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!
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).
In a previous experiment, I created a simple webpage which renders media stored in the URL. This way, it's able to store and render images, audio, and even simple webpages and games. URLs can get quite long, so can store quite a bit of data.
https://mkaandorp.github.io/hdd-of-babel/
So what is the reality? The linked StackOverflow answer claims that, as of 2023, it is "under 2000 characters". How much state can you fit into under 2000 characters without resorting to tricks for reducing the number of characters for different parameters? And what would a rethought approach look like?
Youre doing two things:
1) youre moving state into an arbitrary untrusted easy to modify location.
2) youre allowing users to “deep link” into a page that is deep inside some funnel that may or may not be valid, or even exist at some future point in time, forget skipping the messages/whatever further up.
You probably dont want to do either of those two things.
[0] https://nuqs.dev/
Sure, in the prismjs.com case, I have one of those comments in my code too. But I expect it to break one day.
If a site is a content generator and essentially idempotent for a given set of parameters, and you think the developer has a long-term commitment to the URL parameters, then it's a reasonable strategy (and they should probably formalise it).
Perhaps you implement an explicit "save to URL" in that case.
But generally speaking, we eliminated complex variable state from URLs for good reasons to do with state leakage: logged-in or identifying state ending up in search results and forwarded emails, leaking out in referrer logs and all that stuff.
It would be wiser to assume that the complete list of possible ways that user- or session-identifying state in a URL could leak has not yet been written, and to use volatile non-URL-based state until you are sure you're talking about something non-volatile.
Search keywords: obviously. Seach result filters? yeah. Sort direction: probably. Tags? ehh, as soon as you see [] in a URL it's probably bad code: think carefully about how you represent tags. Presentation customisation? No. A backlink? no.
It's also wiser to assume people want to hack on URLs and cut bits out, to reduce them to the bit they actually want to share.
So you should keep truly persistent, identifying aspects in the path, and at least try not to merge trivial/ephemeral state into the path when it can be left in the query string.
Maybe a solution is some kind of browser widget that displays query params in a user-friendly way that hides the ugliness, sort of like an object explorer interface.
Url query params are not popular in the front end developer world for some reason, probably bc the fundamentals of web dev are often skipped in favor of learning leetcode and all the react hooks. Same could be sade for SQL and CSS.
I also don't think its a good look that the author is a CTO and is just discovering how useful url query params are. that being said, its a pretty good and well-written blog post.