Hacker News new | ask | show | jobs
by fvdessen 241 days ago
I have been using htmx to build a web app and came to the conclusion that it is a dead-end.

The main problem is that the state of your frontend application is in the URL. This is not flexible enough for modern UI where you might have many different zones, widgets, popups, etc. that all need their own local navigation, activation states etc. Putting all of this in a single global url is extremely hard. Designing your app so that you don't need to put it all in the global url is harder.

This problem is trivially solved by React / Vue that all provide their version of a state store that can hold the state, and make it easy as well to have elements shared or not between the tabs of your browser.

If you build your applications like phpBB forum this is not a problem, but nowadays users expect better.

7 comments

> The main problem is that the state of your frontend application is in the URL.

There are plenty of ways to maintain state, including server store, sessions, localstorage, cookies, etc. Say you want the user to be able to customize the app layout: that doesn't need to be in the URL. Now say you provide a search functionality where the user can share the results: now your search criterias definitely should be in the URL.

It's not a black or white, one actually has to think about what the application must achieve.

> modern UI where you might have many different zones, widgets, popups, etc.

This is completely independent from the HTMX matter, but not all your application functionality has to fit one screen / one URL. There's a thin line between "modern" and bloat. Unfortunately this line is crossed every day.

> React / Vue that all provide their version of a state store that can hold the state

And many times they duplicate what's already available server-side.

> There are plenty of ways to maintain state, including server store, sessions, localstorage, cookies, etc. Say you want the user to be able to customize the app layout: that doesn't need to be in the URL. Now say you provide a search functionality where the user can share the results: now your search criterias definitely should be in the URL.

> It's not a black or white, one actually has to think about what the application must achieve.

You are explaining quite well why it's hard to manage the state in a htmx app. As your app grows up all this mumbo jumbo of url, session cookies, cookies, database models becomes tangled spaghetti. You don't have to do any of this in a Vue / React app, and that reduction of complexity alone is worth the weight of those frameworks.

> You don't have to do any of this in a Vue / React app, and that reduction of complexity alone is worth the weight of those frameworks.

Well... I don't know what to say. What you call complexity is what I consider web development 101 really. And it is well worth the price: Better user experience, better performance, less code, better adherence to standards, easier app maintenance and more.

But what did I expect ? These days web developers resort to gigantic dependencies list for the most basic things.

> You don't have to do any of this in a Vue / React app

Something has to do this in an app regardless of what UI framework you're using. Deciding where a particular piece of state lives is fundamental to web development, and yes, URL/session/cookie/database are all valid options depending what kind of state you're storing.

User state is sometimes necessary, but frequently a crutch to avoid answering the real questions. Much of the time, this user-state is at the core of many performance and behavior issues as the user is burdened with unnecessary DOM reorganization. If the layout is dependent on more than just URL parameters and simple server state/cookies, then it begs the question: does this page really have a specific, well-defined purpose? If not, it's ripe for noisy UX, confused users and hard to untangle interface bugs. If so, you probably shouldn't be doing so much on-the-fly DOM configuration. You're not building an OS or window manager here.

Notably, none of this is incompatible with React/Vue where you need it.

Hypermedia can do all of that fine. You don't need to stick it all in the url. Using simple session and cookie/tab id state can be shared and or isolated between tabs. Then just do a lookup in your backend database.

Hypermedia is also way better for realtime and multiplayer.

If anything where HTMX falls short is it doesn't put enough state on the backend, if anything it's not radical enough.

You mean I should be storing the state of a popup menu in my database?
Correct. That's literally what happens with the scroll position, and share modal in this demo (QR code is generated on the fly on the backend):

https://checkboxes.andersmurphy.com

300ms of latency to click a checkbox is a horrible experience, though.
Feels surprisingly good to me.
not really in this case
There is a noticeable delay between interaction and response (~200ms), which is way over the usual 16ms budget for smooth interactions. I think you need some pending state on the client, but that sort of breaks the idea of storing all state on the server haha.
Not sure why this is getting downvoted. I'm literally showing an example of storing popup state in the database as per the parents question.

> You mean I should be storing the state of a popup menu in my database?

No, no you shouldn't.

Well, if you want to present the user with a fully saved UI state even if the user closed your app and opens it later, then yes :)

Otherwise purely client side things should stay either fully client-side, or at most in session storage.

But what really defines client side state?

If the latency was good enough you'd store everything on the server. It doesn't force you to give them the same state when they re-open your app, you can key state by session and tabid if you want.

> But what really defines client side state?

You define it. And the client defines it.

> If the latency was good enough

It's never good enough. Worse, it can abruptly become not good enough. And you have to code additional loading states or optimistic UI for every action that is now performed on the server and takes longer than some time.

> It doesn't force you to give them the same state when they re-open your app

Then why would you store modal state on the server?

It's also a consideration of resource utilization. A million clients with their own app state is better than a million clients hitting your server and requiring you to store that state.

Not if you care about state being consistent between all clients. Say you want a minimap, or a presence indicator, now the server needs to know these things. Same the minute you want backend analytics.

Millions of users hitting the same server at the same time is a very nice problem to have. I've handled 40000r/s (script kiddies are gonna script) with 500+ concurrent users in those demos on a 5$ VPS. With all the scroll position/tab state etc not just going to the server, but to a sqlite db.

Events up are fine if you batch them and 204 (i.e CQRS). In return you get a nice pushed based system that you can throttle/batch. You only push view data when the server decides to. In my case that's every 100ms (because it's a 5$ server), so all changes in that time get batched.

>Whenever your program has to interact with external entities, don't do things directly in reaction to external events. Instead, your program should run at its own pace. Not only does this make your program safer by keeping the control flow of your program under your control, it also improves performance for the same reason (you get to batch, instead of context switching on every event). Additionally, this makes it easier to maintain bounds on work done per time period.

- tiger style https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TI...

You don't need optimistic UI, a fast server with an in process DB and a decent backend language and you'll be fine for a lot of use cases. I like to add a pop animation on the client to register something has happened, but in a lot of situations you don't even need that.

I’ve heard of a major fintech in South America that stores all the client state on the backend. Millions of users daily and it works
It's a dead end for your use case, let's be very clear about that.

And it's funny that you think anything about React and/or Vue is 'trivial'.

Surely you’re not saying the frameworks famous for ui = f(state) actually suck at managing state…
If it only were true. React is nothing like ui = f(state). More like ui = f(some_trivial_state) + lifecycles magic + probably global_state.

Garbage. But effective devrel.

This type of dismissive attitude is so strange to me. The only reason "lifecycles magic + probably global_state" would be causing your app to behave unpredictably is - this is going to shock you - because you closed your mind to a tool by dismissing it as garbage before you used it, and then failed to use it properly because you think its popularity boils down to PR.

For instance, you could entirely forgo the influence of lifecycles and global state by putting everything in a top-level Context with 1 state object that you only ever update by calling `setState`.

After that, you might find reasons to optimize your app, which could lead you to more interesting approaches like reducers or state management libraries or memoization, but I'm guessing you would never get that far and just go back to what you were doing before, since YOUR preferences are battle hardened and reliable software, while things you don't know about are only popular because of Facebook. Obviously.

So, redux? It's either redux or, sorry, "lifecycles magic + probably global_state". And who uses redux in 2025?
Honestly, if you’re getting thru life with this attitude, good for you, but you might want to consider if it’s the only way
kinda does, tbh.
React and Vue does not solve anything users expect.
> This problem is trivially solved by React / Vue that all provide their version of a state store that can hold the state

What exactly are you talking about, for React? What provides "a state store"?

maybe for very complex use cases, but I'm using htmx(and unpoly) + alpinejs + localstorage and still didn't find a case that doesn't fit.
have you tried a native language with a networking and gui library to achieve the same thing?