[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)