URL-Driven State in HTMX

lorenstew.art

293 points by lorenstewart 2 days ago


Twey - a day ago

The example URL here, though, is still not (helpfully) bookmarkable because the contents of page 2 will change as new items are added. To get truly bookmarkable list URLs, the best approach I've seen is ‘page starting from item X’, where X is an effectively-unique ID for the item (e.g. a primary key, or a timestamp to avoid exposing IDs).

podgorniy - a day ago

I think people are now ready for php. I bet it will be reinvented on top of nodejs.

gortok - 21 hours ago

So until about 2013? 2014? URL-driven state was just the way everything worked.

One of the major complaints of `cgi-bin` was that you had to manually add back to the URL to manage state (and of that time, there were a good number of cgi-bin applications that just didn't bother -- which unsurprisingly is how the SPAs worked at first until "URL Routing" took over).

But, all of this is literally just reinventing the wheel that's been there since the web began. The entire purpose of the web was to be able to link to a specific resource, action, or state without having to to anything other than share a URL.

What's wild is there are whole generations of programmers that started programming after the SPA world debuted and are now re-learning things that "were just the way things were" before 2013.

DimmieMan - a day ago

The JS world leaves me more and more perplexed.There's a similar rant about forms, but why is this so hard? Huge amount of dev time spent being able to execute asynchronous functions to the backend seamlessly yet pretty much every major framework is just rawdog the url string and deal with URLSearchParams object yourself.

Tanstack router[1] provides first class support for not only parsing params but giving you a typed URL helper, this should be the goal for the big meta frameworks but even tools like sveltekit that advertise themselves on simplicity and web standards have next to zero support.

I've seen even non JS frameworks, with like fifteen lines of documentation for half baked support of search params.

The industry would probably be better off if even a tenth of the effort that goes into doing literally anything to avoid learning the platform was spent making this (and post-redirect-get for forms) the path of least resistance for the 90% of the time search params are perfectly adequate.

I don't use HTMX but i do love how it and its community are pushing the rediscover of how much simpler things can be.

[1] https://tanstack.com/router/latest/docs/framework/react/guid...

Arch-TK - a day ago

Next you'll tell me the URL can contain information which can be used to auto-scroll you to a specific heading.

PaulHoule - a day ago

This is a classic pattern of web applications from the 1990s. Works amazingly well even w/o HTMX

btown - a day ago

> treating URL parameters as your single source of truth... a URL like /?status=active&sortField=price&sortDir=desc&page=2 tells you everything about the current view

Hard disagree that there can be a single source of truth. There are (at least) 3 levels of state for parameter control, and I don't like when libraries think they can gloss over the differences or remove this nuance from the developer:

- The "in-progress" state of the UI widgets that someone is editing (from radio buttons to characters typed in a search box)

- The "committed" state that indicates the snapshot of those parameters that is actively desired to be loaded from the server; this may be debounced, or triggered by a Search button

- The "loaded" state that indicates what was most recently loaded from the server, and which (most likely) drives the data visualized in the non-parameter-controlling parts of the UI

What if someone types in a search bar but then hits "next page" - do we forget what they typed? What happens if you've just committed changes to your parameters, but data subsequently loaded from a prior commit? Do changes fire in sequence? Should they cancel prior requests or ignore their results? What happens if someone clicks a back button while requests are inflight, or while someone's typed uncommitted values into a pre-committed search bar? How do you visualize the loaded parameters as distinct from the in-progress parameters? What if some queries take orders of magnitude longer than others, and you want to provide guidance about this?

All of those questions and more will vary between applications. One size does not fit all.

If this comment resonates with you, choose and advocate for tooling that gives you the expressivity you feel in your gut that you'll need. Especially in a world of LLMs, terse syntax and implicit state management may not be worth losing that expressivity.

colgandev - 14 hours ago

HTMX has given me so much joy. I love Django and was there near the beginning. For a while I thought I had to switch to something like FastAPI and Vue etc to make relevant web apps and sites, but with Django recently adding async, Django Ninja, and HTMX, I'd reach for Django now for almost anything besides a few specific use cases.

So many problems I've run into with newer tools feel like they were already solved years ago if you can SSR.

Not that the newer tools don't have their place and they have plenty of good ideas, but it's been fun to see Django stay relevant and for more of the included batteries to be useful again (forms, templates, etc).

TimTheTinker - a day ago

I had a similar strategy when building early web apps with jQuery and ExtJS (but using the URL hash before the History API was available). Just read from location.hash during page load and write to it when the form state changes.

For more complex state (like table layout), I used to save it as a JSON object, then compress and base64 encode it, and stick it in the URL hash. Gave my users a bookmarklet that would create a shortened URL (like https://example.com/url/1afe9) from my server if they needed to share it.

_heimdall - a day ago

This is a great pattern to follow, and I highly recommend understanding it even to those working on projects that are full client-side SPAs.

Its too easy to jump right in to react, NextJS, etc. Learning why URLs work the way they work is still extremely useful. Eventually you will want to support deep linking, and when you do the URL suddenly becomes really important.

Remix has also been really interesting on this front. They leaned heavily into web standards, including URLs as a way of managing state.

junto - a day ago

I find it deeply ironic that we have come full circle, and Javascripters have reinvented what we had 20 years ago.

vyrotek - a day ago

The syncing of the state reminded me a lot of datastar Signals. And a little bit of ASP.NET ViewState.

https://data-star.dev/guide/reactive_signals

cloudking - a day ago

I like the simplicity. I've been building some web apps with Alpine.js recently, another lightweight React alternative. It's pretty powerful and capable for building reactive SPAs, only ~16kb.

https://alpinejs.dev/

https://github.com/alpinejs/alpine

user____name - a day ago

This is cute but merely points to the obvious solution of base64 encoding all page contents straight into the URL.

Gilipe - a day ago

Our dealership listings page is largely run with this pattern, as well as most of our plugins. Nothing new and very dependable. Forgo HTMX for one less dependency.

ac130kz - a day ago

>URLs up to ~2000 characters

Exactly, this approach doesn't scale well without trickery involved. You have to have some sort of weird encoding in place to compact it down.

torstenvl - a day ago

Ideally, this is how state management should work all the time, regardless. Holding too much state server-side breaks bookmarks and shares.

EDIT: Hmm. Is this comment controversial? Obviously some people disagree strongly. Mind sharing why?

AdieuToLogic - a day ago

On a related note, I've found combining htmx with Parsley[0] to be very powerful for adding client-side validation to declarative server-rendered HTML form definitions. All that is needed is a simple htmx extension[1] and applicable data attribute use.

0 - https://parsleyjs.org/doc/index.html

1 - https://htmx.org/docs/#extensions

jarofgreen - a day ago

> SEO is built in since search engines can crawl every state combination.

This isn't always a plus - bots can find a very large number of pages to crawl and swamp your server with traffic. Maybe they would get stuck on all the combinations of listing page filters and miss the important pages.

Not saying the conclusion is wrong - just something to consider.

hamdouni - a day ago

Funny to see query parameters used as intended ... few decades ago

justsayinnnn - a day ago

Finally we are getting out of Hipster tech territory and back to something that makes sense. Thank you HTMX.

teddyh - 19 hours ago

I remember that before cookies were widely implemented in web browsers, the Spinner web server (effectively an early web server and development framework in one process) implemented what it called ”prestate”; a parenthesised portion of the URL, part of the path, but before the actual path. Like this: http://example.com/(tables,images)/developers/

gloosx - a day ago

It's just how web works – storing data in URL params to restore the same state later. With React or whatever library you do the exactly same thing. In this case HTMX doesn't particularly stand out or enable anything new here.

aitchnyu - a day ago

My HTMX side project uses Rison to store nested objects without unreadable encoding or handmade parsing.

https://github.com/Nanonid/rison

theanonymousone - a day ago

I'm currently writing a single-file web application in "Java" using JBang, J2Html, and Javalin (+htmx).

It's so refreshing!

bravesoul2 - a day ago

Please do! Dropping deep links with state is very useful.

It cant be used for everything. E.g. not dark mode!

tomtomistaken - a day ago

We were experimenting with saving states in URLs with https://libmap.org. Sharing these URLs on mastodon.social would add the post to the map.

rorylaitila - a day ago

I didn't see if you were doing this, but there is an additional use case that I had when using hot swapping like HTMX: updating other links on the page to reflect the URL state, when those links are outside of the swapped content.

While the server can use the URLs to update the links in the new HTML, if other links outside that content should also reflect the changes params, you need to manually update them.

In my progressive enhancement library I call this 'sync-params' https://github.com/roryl/zsx?tab=readme-ov-file#synchronize-...

- a day ago
[deleted]
robertoandred - 19 hours ago

> When you move from React to HTMX, you trade complex state management for server-side simplicity.

Managing the same state will have the same complexity on the server as it does on the client. HTMX's smugness is a huge turnoff.

emmelaich - a day ago

Another reminder of Programming inside the URL String. https://apenwarr.ca/log/20121218

kazinator - a day ago

URL-driven state? Sounds like a "take that" upper cut to the jaw of the RESTful opponent.

cadamsdotcom - a day ago

This pattern - saving the query to the URL with the history API - is fantastic UX but never gets implemented because there’s never time. Luckily an LLM can build this quickly as it’s straightforward and mostly boilerplate.

Still the boilerplate makes me wonder if it belongs in a library, eg. a React hook that’s a drop in replacement for `useState`. Backend logic would still need to be implemented. Does something like this exist?

o11c - a day ago

Note that you can store longer state (at least 64K; more not tested) in the fragment (`location.hash`); obviously only the client gets to see this, but it's better than nothing (and JS can send it to the server if really needed).

For parameters the server does need to see, remember that params need not be &-separated kv pairs, it can be arbitrary text. Keys can usually be eliminated and just hard-coded in the web page; this may make short params longer but likely makes long ones shorter.

You absolutely should not restore state based on LocalStorage; that breaks the whole advantage of doing this properly! If I wanted previous state, I would've navigated my history thereto. I hope this isn't as bad as sites that break with multiple open tabs at least ...