[HN Gopher] URLs are state containers
___________________________________________________________________
URLs are state containers
Author : thm
Score : 300 points
Date : 2025-11-02 11:12 UTC (11 hours ago)
(HTM) web link (alfy.blog)
(TXT) w3m dump (alfy.blog)
| zkmon wrote:
| 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.
| Scarblac wrote:
| But from the viewpoint of a web app where you navigate between
| different (versions of) pages, the state of that app can be the
| address of the currently displayed page.
| zkmon wrote:
| It's the state of your browser, not the app. App could be
| serving different pages to different clients at the same
| time.
| layer8 wrote:
| State is just your location in state space.
| zkmon wrote:
| An address book is not "state space". The country, land and
| things are the state.
| layer8 wrote:
| Not every location represents a state, but every state can
| be considered a location.
|
| If you want to argue against the use of URLs to represent
| state, I would concentrate on the "R" (resource) aspect.
| zkmon wrote:
| I think you are talking about client's navigational
| state. The original title of this post was "app state
| ...". Still it is not clear about state of what.
|
| Navigational state need not be confused with app state.
| Also talking about "state" as in "state machine" etc used
| to sound pretty academic with obscure meaning of the word
| "state". When someone says "state machine" they are
| basically saying "I'm a PhD and you are not". There are
| simpler and more crisp ways to convey things rather than
| via obscurity.
| vbezhenar wrote:
| 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.
| oceanplexian wrote:
| I think it depends on the permanence of the thing you're
| keeping state for. For example for a blog post, you might want
| to keep it around for a long time.
|
| But sometimes it's less obvious how to keep state encoded in a
| URL or otherwise (i.e for the convenience of your users do you
| want refreshing a feed to return the user to a marker point in
| the feed that they were viewing? Or do you want to return to
| the latest point in the feed since users expect a refresh
| action to give them a fresh feed?).
| tomtomistaken wrote:
| You can always do versioning.
| bob1029 wrote:
| 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.
| qlm wrote:
| Maybe I'm misunderstanding what you're saying but applications
| like this tend to be horrible to use. How do you handle
| somebody navigating in two tabs at once? What about the back
| button?
| __MatrixMan__ wrote:
| Agreed. Also, when you paste somebody a URL, they should see
| what you saw... if at all possible.
| mewpmewp2 wrote:
| Also bookmarks etc? For example if you have a view where you
| can have complex filters etc, you may want to bookmark this.
| afiori wrote:
| I guess they use something like sessionStorage to hold tab
| specific ids.
|
| But something that can bite you with these solutions if that
| browsers allow you to duplicate tabs, so you also need some
| inter-tab mechanisms (like the broadcast API or local storage
| with polling) to resolve duplicate ids
| skrebbel wrote:
| This entire article is an argument _against_ your approach
| here, and you 're not really addressing any of its points.
|
| Basically, your approach is easier to code, and worse to use.
| Bookmarks, multiple tabs, the back button, sharing URLs with
| others, it all becomes harder for users to do with your design.
| I mean feel free, because with many tech stacks it _is_ indeed
| easier, but don 't pretend it's not a tradeoff. It's easier and
| worse.
| alex_duf wrote:
| Any blob of byte is a state container
| padolsey wrote:
| 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.
| weikju wrote:
| > Generally, even the most internet-savvy age group, are vastly
| ill-equipped.
|
| It's a losing battle when even the tools (web browsers hiding
| URLs by default, heck even Firefox on iOS does it now!) and
| companies (making posters with nothing more than QR codes or
| search terms) are what they're up against....
| Lord-Jobo wrote:
| And with commercial software like Outlook being so ubiquitous
| and absolutely HORRENDOUS with url obfuscation, formatting,
| "in network" contacts, and seemingly random spam filtering.
|
| Our company does phishing tests like most, and their
| checklist of suspicious behavior is 1 to 1 useless. Every
| item on the list is either 1: something that our company
| actually does with its real emails or 2: useless because
| outlook sucks a huge wang. So I basically never open emails
| and report almost everything I get. I'm sure the IT
| department enjoys the 80% false report rate.
| loloquwowndueo wrote:
| 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!
| wild_egg wrote:
| With a custom compression dictionary made against your JSON
| schema, I would bet you could still pack a surprising amount of
| data into 256 bytes that way.
| skrebbel wrote:
| I tried this once and discovered that for us it worked even
| better when populating the dictionary with a bunch of
| commonly seen URLs. Like that includes the same field names
| as the json schema, but none of the other JSON Schema cruft,
| and it also includes commonly used values etc. It seemed like
| the smarter I tried to be, the worse the results got.
|
| I just used Pako.js which accepts a `{ dictionary: string }`
| option. Concat a bunch of common URL together, done.
|
| The only downside (with both our approaches) is if you add
| substantially many new fields / common values later on, you
| need to update the dictionary, and then old URLs don't work,
| so you'd need some sort of versioning scheme and use the
| right dictionary for the right version.
| sehugg wrote:
| My 8-bit IDE lets you share your ROM as a lzg/b64-encoded URL.
| Things get dicey when you go above 2000 characters or so.
| jcparkyn wrote:
| 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.
|
| https://scrobburl.com/ https://github.com/Jcparkyn/scrobburl
| qdotme wrote:
| 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.
|
| https://pdfrobots.com/robot/beta/#qNkfQYfYQOTZXShZ5J0Rw5IBgB...
| caseysoftware wrote:
| HATEOAS never gets the love it deserves until you call it
| something else..
|
| Probably because it sounds like the most poorly named breakfast
| cereal ever.
| cluckindan wrote:
| This has nothing to do with HATEOAS. Well, apart from both
| using URLs. But HATEOAS really isn't about storing state in
| URLs.
| btown wrote:
| I mean, at the end of the day it _is_ a cerealization format...
| cluckindan wrote:
| Jokes aside, the crux of HATEOAS is having a dumb frontend
| which just displays content and links from backend responses.
| All logic is on the server side. It is more like a terminal
| connection than a browser based application.
| tsimionescu wrote:
| Not at all. HATEOAS is about defining data formats that the
| client and server agree on ahead of time.
|
| Browsers running Javascript referenced from HTML is a
| perfect example of HATEOAS, for example. browsers and web
| server creators agreed on the semantics of these two data
| formats, and now any browser in the world can talk to any
| web server in the world and display what was intended to be
| displayed to the user.
|
| If the web design hadn't been HATEOAS, you'd need server
| specific code in your browser, like AOL had a long time
| ago, where your browser would know how to look up specific
| parts of the AOL site and display them. This is also how
| most client apps are developed, since both the client and
| the server are controlled by the same entity, and there is
| no problem in hardcoding URLs in the client.
| MyOutfitIsVague wrote:
| 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.
| sixhobbits wrote:
| More good content with a bunch of GPT noise added, obvious from
| patterns like
|
| 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?
| tliltocatl wrote:
| Is it really an LMM? It's not like real humans can't write the
| same style, LLMs have picked up on an existing stylistic
| tendency. I hate these patterns as much as anyone, and I have
| noticed them since long before transformers were a thing.
| ahmadalfy wrote:
| 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.
| jorl17 wrote:
| 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 wrote:
| > 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 wrote:
| The 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 wrote:
| One example I think is super interesting is the NWS Radar
| site, 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_eyJhZ2VuZGEiOnsiaWQiO.
| ..
|
| Which, if you take the base64 encoded string, strip off the
| control characters, pad it out to a valid base64 string, you
| get
|
| "eyJhZ2VuZGEiOnsiaWQiOm51bGwsImNlbnRlciI6Wy0xMTUuOTI1LDM2LjAw
| Nl0sImxvY2F0aW9uIjpudWxsLCJ6b29tIjo2LjM1MzMzMzMzMzMzMzMzMzV9L
| CJhbmltYXRpbmciOmZhbHNlLCJiYXNlIjoic3RhbmRhcmQiLCJhcnRjYyI6Zm
| Fsc2UsImNvdW50eSI6ZmFsc2UsImN3YSI6ZmFsc2UsInJmYyI6ZmFsc2UsInN
| 0YXRlIjpmYWxzZSwibWVudSI6dHJ1ZSwic2hvcnRGdXNlZE9ubHkiOmZhbHNl
| LCJvcGFjaXR5Ijp7ImFsZXJ0cyI6MC44LCJsb2NhbCI6MC42LCJsb2NhbFN0Y
| XRpb25zIjowLjgsIm5hdGlvbmFsIjowLjZ9fQ==", which decodes into:
|
| {"agenda":{"id":null,"center":[-115.925,36.006],"location":nu
| ll,"zoom":6.3533333333333335},"animating":false,"base":"stand
| ard","artcc":false,"county":false,"cwa":false,"rfc":false,"st
| ate":false,"menu":true,"shortFusedOnly":false,"opacity":{"ale
| rts":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/
| asielen wrote:
| In this case, why encode the string instead of just having
| the options as plain text parameters?
| qdotme wrote:
| Nesting, 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]}]}
| toxik wrote:
| Sorry but this is legitimately a terrible way to encode
| this data. The number 0.8 is encoded as base64 encoded
| ascii decimals. The bits 1 and 0 similarly. URLs should not
| be long for many reasons, like sharing and preventing them
| from being cut off.
| yawaramin wrote:
| The OP gives great guidance on these questions.
| thijsvandien wrote:
| While 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 wrote:
| JS 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 wrote:
| I'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 wrote:
| Does 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 wrote:
| It'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 wrote:
| In 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.
| nkrisc wrote:
| As a UX designer, this is a failure of the UX designers,
| IMO. If you're a UX designer for web, you should be aware
| of web technology and be thinking about these things.
| Even if you don't know enough to fully specify it, you
| should be able to enough such that you can have
| conversations with a developer to work together to fully
| spec it out.
|
| That said, I've also worked with some developers that
| didn't like intruding on their turf, so to speak. Though
| I've also worked with others that were more than happy to
| collaborate and very proactive about these sorts of
| things.
|
| Furthermore, as a UX designer this is the sort of topic
| that we're unlikely to be able to meaningfully discuss
| with PMs and other stakeholders as it's completely non-
| visual and often trying to bring this up with them and
| discuss it ends up feeling like pulling teeth and them
| wondering why we're even spending time on it. So usually
| it just ended up being a discussion between me and the
| developers with no PM oversight.
| moritzwarhier wrote:
| Nothing weird about it, you see people arguing right here
| whether a site should add a new history entry when a
| filter is set.
|
| Interacting with the URL from JS within the page load
| cycle is inherently complex.
|
| For what it's worth, I'd also argue that the right
| behavior here is to _replace_.
|
| But that of course also means that now the URL on the
| history stack for this particular view will always have
| the filter in it (as opposed to an initial visit without
| having touched anything).
|
| Of course the author's case is the good/special one where
| they already visited the site with a filter in the URL.
|
| But when you might be interested in using the view/page
| with multiple queries/filters/paramerers, it might also
| be unexpected: for example, developers not having a
| dedicated search results page and instead updating the
| query parameters of the current URL.
|
| Also, from the history APIs perspective, path and query
| parameters are interchangeable as long as the origin
| matches, but user expectations (and server behavior)
| might assign them different roles.
|
| Still, we're commenting on a site where the main view
| parameter (item ID, including submission pages) is a
| query parameter. So this distinction is pretty arbitrary.
|
| And the most extreme case of misusing _pushState_
| (instead if replace) are sites where each keystroke in
| some typeahead filter creates a new history entry.
|
| All of this doesn't even touch the basic requirement that
| is most important and addressed in the article: being
| able to refresh the page without losing state and being
| able to bookmark things.
|
| Manually implementing stuff like this on top of a basic
| routing functionality (which should use pushState) in an
| SPA is complex very quickly.
| macNchz wrote:
| It's definitely _possible_ to make a really stellar
| experience, but that winds up being the exception. The
| URL and history state are sort of "invisible" elements of
| the user experience but require thoughtful care and
| attention to what the user expects /wants at each step, a
| level of attention which is already a rarity in web
| development even in the most visible parts of a page...so
| frequently the history/back button stuff just totally
| sucks.
| embedding-shape wrote:
| Yeah, in my experience you only get great stuff when both
| product and engineering has equal care for the final
| experience. If either parties lack care, you'll miss
| stuff, particularly things that are "invisible" as you
| say.
| DrewADesign wrote:
| I think it's a "use the right tool for the job" thing.
| Putting ephemeral information like session info in URLs sucks
| and should only be done if you need to pass it in a get
| request from a non-browser program or something, and even
| then I think you should redirect or rewrite the url or
| something after the initial request. But I think actual
| navigational data or some sort of state if it's in the middle
| of an important action is acceptable.
|
| But if you really just want your users to be able to hit
| refresh and not have their state change for non-navigational
| stuff like field contents or whatever, unless you have a
| really clear use case where you need to maintain state while
| switching devices and don't want to do in server-side, local
| storage seems like the idiomatic choice.
| hamdingers wrote:
| Browser autocomplete behavior is reliably incorrect and
| infuriating either way, so it's not a good reason to avoid
| the utility of having bookmarkable/sharable urls.
| SoftTalker wrote:
| Yeah it's an annoyance more than it helps. I always disable
| it.
| noir_lord wrote:
| I do as well - it's just irritating.
|
| Same with search ahead.
| Dwedit wrote:
| My personal take would be if it takes you to what's basically
| another page (such as the entire page being rewritten), then
| involve browser history.
| bgilroy26 wrote:
| To 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 wrote:
| Because 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 wrote:
| They probably have meant something like base64 encode
| linked_list wrote:
| If you base64 encode an ascii string it gets 33% longer
| poncho_romero wrote:
| This 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 wrote:
| I 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.
| cyptus wrote:
| and where is the hash mapped back again?
| Waterluvian wrote:
| The 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 wrote:
| I can understand "shareable" state (scroll position), but _as
| much as possible_ seems like overkill.
|
| Why not just use localStorage?
| layer8 wrote:
| > 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 wrote:
| You could work around that if needed with a unique id per
| tab (I was curious myself)
|
| https://stackoverflow.com/questions/11896160/any-way-to-
| iden...
| layer8 wrote:
| Yes, 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 wrote:
| Scroll position is _kind of_ fine. Typically I can link
| the ID in the URL as "state".
|
| I was referring to mostly everything else
| phillipseamore wrote:
| sessionStorage should treat the windows/tabs as separate
| fittom wrote:
| I 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 wrote:
| As 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 wrote:
| As 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.
| lenkite wrote:
| To 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 wrote:
| > 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 wrote:
| 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.
| apitman wrote:
| > 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 wrote:
| Also 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 wrote:
| The 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 wrote:
| Restoring 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.
| endless1234 wrote:
| Still leaves the problem of not being able to simply send the
| current URL to someone else and know they'll see the same
| thing. Of course anchors can solve this, but not
| automatically
| pests wrote:
| Chrome (at least?) solves this via Text Fragments[0] which
| are a pure client side thing and requires no server or site
| support.
|
| This URI for example:
|
| https://developer.mozilla.org/en-
| US/docs/Web/HTML/Reference/...
|
| Links to an instance of "The Referer" narrowed down via a
| start prefix ("downgrade:") and end suffix ("to origins").
|
| These are used across Google I believe so many have
| probably seen them.
|
| [0] https://developer.mozilla.org/en-
| US/docs/Web/URI/Reference/F...
| o11c wrote:
| Even with JS, if it is classical synchronous JS it is much
| better than the modern blind push for async JS, which causes
| the browser to try to restore the position before the JS has
| actually created the content.
| nextaccountic wrote:
| isn't there a way to instruct the browser to restore the
| position only after certain async thing?
| smrtinsert wrote:
| 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.
| mk12345 wrote:
| 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.
|
| https://mkaandorp.github.io/hdd-of-babel/
| yubblegum wrote:
| 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.
| croes wrote:
| Urls have extra parts like the parameters to store that data.
| It's not a hack
| azangru wrote:
| > 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?
| djoldman wrote:
| Each of those characters (aside from domain) could be any of 66
| unique ones: Uppercase letters: A through Z
| (26 characters) Lowercase letters: a through z (26
| characters) Digits: 0 through 9 (10 characters)
| Special: - . _ ~ (4 characters)
|
| So you'd get a lot of bang for your buck if you really wanted
| to encode a lot of information.
| croes wrote:
| Unless you have some kind of mapping to encode different
| states with different character blocks your possibilities are
| much more limited. Like storing product ids or EAN plus the
| number of items. Just hope the user isn't on a shopping spree
| noodletheworld wrote:
| Mmm.
|
| 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.
| WilcoKruijer wrote:
| nuqs[0] is a great (React) library for managing state inside of
| the URL.
|
| [0] https://nuqs.dev/
| ahmadalfy wrote:
| This is the first time I see this, thanks for sharing it
| tjpnz wrote:
| 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.
| exasperaited wrote:
| 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.
| jakegmaths wrote:
| The latest version of Microsoft Teams is absolutely terrible at
| this... just one URL for everything. No way to bookmark even a
| particular team.
| jakegmaths wrote:
| I use URLs for pixel art: https://www.mathsuniverse.com/pixel-
| art?p=GgpUODLkg-N0JchwOF...
| jordanpg wrote:
| 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.
| nathsav wrote:
| you can save so much data in the url, I like how pocketcal.com
| stores the calendar informations
| simonhamp wrote:
| React kid discovers the web
| game_the0ry wrote:
| 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.
| simonhamp wrote:
| No snark. Genuinely happy. This is progress
| ttoinou wrote:
| 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
| adolph wrote:
| _Hanselman famously said "URLs are UI" and he's absolutely right_
|
| A challenge for this is that the URL is the most visible part of
| an HTTP request but there are many other submerged parts that are
| not available as UI yet are significant to the http response
| composition.
|
| Additionally, aside from very basic protocol, domain, and path,
| the URL is a very not human friendly UI for composing the state.
| superkuh wrote:
| >Scott Hanselman famously said "URLs are UI"
|
| I actually implemented a comment system where users just pick any
| arbitrary URL on the domain, ie, http://exampledomain.com/, and
| append /@say/ to the URL along with their comment so the URL _is_
| the UI. An example comment would be typed in the URL bar like,
|
| http://exampledomain.com/somefolder/somepage.html/@say/Hey! Cool
| somepage. - Me
|
| And then my perl script tailing the webserver log file sees the
| line and and adds the comment "Hey! Cool somepage. - Me" to the
| .html file on disk for comments.
| zkmon wrote:
| You are either changing the meaning of "state", or probably
| unaware of what it means. To start with, state of what? app (http
| server) or the http client?
| yoavm wrote:
| I think the author is referring to the state of the form.
| zkmon wrote:
| State of the form is it's data.
| tomtomistaken wrote:
| I use the concept for https://libmap.org to save the state of the
| map. You can share the libmap link via mastodon social or bluesky
| to make it permanent.
|
| This is a small hobby project, I am not in IT.
| jwpapi wrote:
| As an application developer I think this is very good advice and
| I wish I wouldve be more strict about it earlier.
| chaboud wrote:
| If the URL is your state container, it also becomes a leakage
| mechanism of internals that, at the very least, turns into a
| versioning requirement (so an old bookmark won't break things).
| That also means that there's some degree of implicit assumption
| with browsers and multi-browser passing. At some point, things
| might not hold up (Authentication workflows, for example).
|
| That said, I agree with the point and expose as much as possible
| in the URL, in the same way that I expose as much as possible as
| command line arguments in command line utilities.
|
| But there are costs and trade offs with that sort of
| accommodation. I understand that folks can make different design
| decisions intentionally, rather than from ignorance/inexperience.
| ravenical wrote:
| Letterboxd does this really well - each view is its own page!
| It's so pretty compared to other sites
| jarofgreen wrote:
| Also to consider: bot traffic and SEO.
|
| Depending on which mechanism you use to construct your state URLs
| they will see them as different pages, so you may end up with a
| lot of extra traffic and/or odd SEO side effects. For SEO at
| least there are clear directives you can set that help.
|
| Not saying you shouldn't do this - just things to consider.
| Ayesh wrote:
| Canonical URLs come to the rescue.
| jarofgreen wrote:
| Only for SEO - they don't help at all with aggressive AI
| scraper bots.
| jFriedensreich wrote:
| Hot module replacement masks a lot of annoyances for end users.
| Yes its more instantaneous than reloading a page and relying on
| urls for all of the state and I am not advocating hard for
| abolishing HMR anymore, but it would be nice if we still used way
| more url state than currently the case. Browsers will also
| hibernate tabs to varying degrees, server sessions expire all the
| time, things are not shareable. The only thing that works as
| users expect is url state. One thing i absolutely hate about ios
| apps is how every state is lost if i just have the app in the
| background for a few seconds, this even applies to major apps
| like youtube, google maps, many email clients etc. Why do we live
| in this stupid world were things are not getting better, just
| because someone made things more convenient for developers?
|
| PS: and i curse the day the social media brainwashed marketing
| freak coined the term "deep link" to mean just a normal link as
| its supposed to work.
| SoftTalker wrote:
| Modern browsers have an "open clean link" feature that strips all
| the query parameters (everything after the '?' character in the
| URL).
|
| This is because many sites cram the URL full of tracking IDs, and
| people like to browse without that.
|
| So if you are embedding state in your URL, you probably want to
| be sure that your application does something sane if the browser
| strips all of that out.
| Ayesh wrote:
| > Everything after the '?' character.
|
| It only strips known tracking parameters b(like those utm_
| query params). It does not remove all parameters; if that's the
| case, YouTube video links will stop working.
| SoftTalker wrote:
| Hm, I didn't know that. Seems very easy to game then, just
| change your tracking parameter name to one that the browser
| doesn't strip.
| mrbonner wrote:
| I believe draw.io achieves complete state persistence solely
| through the URL. This allows you to effortlessly share your
| diagrams with others by simply providing a link that contains an
| embedded Base64-encoded string representing the diagram's data.
| However, I'm uncertain whether this approach would qualify as a
| "state container" according to the definition presented in the
| article.
| ronreiter wrote:
| Duh :)
| aatd86 wrote:
| Finishing building a framework at the moment. I'd rather say that
| they are state descriptors... They don't contain all the state.
| But they are some kind of hashkey that allow to retrieve
| application state. "Hypertext as the engine of application
| state."
| smadge wrote:
| One might even say that hyperlinks are the engine of application
| state.
| njacobs5074 wrote:
| I'm not certain that I agree with this because a URL makes no
| claims about idempotency or side-effects or many other behaviors
| that we take for granted when building systems. While it is
| possible to construct such a system, URLs do not guarantee this.
|
| I think the fundamental issue here is that semantics matter and
| URLs in isolation don't make strong enough guarantees about them.
|
| I'm all for elegant URL design but they're just one part of the
| puzzle.
| mattlondon wrote:
| Yes It does. HTTP PUT is idempotent.
| nonethewiser wrote:
| >If you need to base64-encode a massive JSON object, the URL
| probably isn't the right place for that state.
|
| Why?
|
| I get it if we're talking about a size that flirts with browser
| limitations. But other than that I see absolutely no problem with
| this. In fact it makes me think the author is actually
| underrating the use-case of URL's as state containers.
| dzhar11 wrote:
| Recommendation:
|
| https://github.com/Nanonid/rison
|
| Super old but still a very functional library for saving state as
| JSON in the URL, but without all the usual JSON clutter. I first
| saw it used in Elastic's Kibana. I used it on a fancy internal
| React dashboard project around 2016, and it worked like a charm.
|
| Sample: http://example.com/service?query=q:'*',start:10,count:10
| due-rr wrote:
| I use this for my rss reader!
|
| https://rssrdr.com/?rss=raw.githubusercontent.com/Roald87/Ha...
| alansaber wrote:
| This is something you learn to appreciate when you do web
| scraping. I do overlook it for frontend webdev though
| notepad0x90 wrote:
| Deeplinking is awesome! The Azure portal is my favorite example.
| You could be many layers deep in some configuration "blade" and
| the URL will retain the exact location you are in the UI.
| XCSme wrote:
| > #/dashboard - Single-page app routing (though it's rarely used
| these days)
|
| I actually use that for my self-hosted app, because hash routing
| doesn't require .htaccess or other URL rewriting functionality
| server-side. So yes, it's not ideal, but you don't fully control
| the deployment environment, it's better to reduce as much as you
| can the requirements.
___________________________________________________________________
(page generated 2025-11-02 23:00 UTC)