imo any experienced frontend framework person will be able to pick out the issues with this impl. OP is returning entire strings of HTML (App, Todo, Icon) to rerender on state changes. this works when 1) you dont care about keeping UI state on the parts that are replaced (incl any stateful children of the UI element that happen to be there in your DOM structure), and 2) when you dont have to update the app in any other places when your state changes. go ahead and build your whole app by replacing innerHtml, frontend frameworks will be right here when you get back from speedrunning the last 10 years.
in other words, this todo app is just about the most complex of a frontend you can easily* build with htmx, i'm afraid. try to fix either 1 or 2 and you end up building components and your own little framework.
as an exercise to demonstrate, try taking OPs code and adding a count of todos on each All/Active/Completed tab that should update every time u add/edit/delete the todos. see how much extra ui code that takes. compare with equivalent [framework of choice] impl (in most it will just involve 1 state update, thats it). this is htmx's explosion of complexity that makes it not [ optimized for change ] (https://overreacted.io/optimized-for-change/). code that is hard to change eventually calcifies and consumes code that is easy to change if you do not consistently garbage collect (nobody does)
i bought the hype too until i tried building something nontrivial in htmx and im afraid the aforementioned islands of interactivity you can build are very very smol islands indeed.
happy to revisit my opinion if there are componentlike design patterns in htmx i am not aware of.
*emphasis on easily; with enough elbow grease u can do anything ofc. but then you fall out of htmx's very narrow [ pit of success ](obligatory codinghorror dot com link)
In my decades of experience building web applications, I have found it exceedingly rare for components to benefit from SPA-style state management. These frameworks build layers of abstractions to replace functionality that exists in the browser. For example, web forms should never require the level of complexity that frameworks like React steer developers towards: downloading JS components, listening to events to build a local state, crafting an AJAX request, rendering the return JSON as HTML. It almost seems as though there is an entire generation of frontend developers who never learned that there are way simpler alternatives that perform just as well in the real world. htmx might not end up being a SPA-killer, but progressive enhancement has always been a worthy contender.
> abstractions to replace functionality that exists in the browser.
I was just having the conversation yesterday. We were talking about how React Query/Tanstack Query is a bit of a rats nest under the hood. We're looking into ways of taking it out to simplify things a bit, especially since we aren't using a ton of the library's features, nor will ever have a need for them. Found myself saying "I wish the native fetch() had a built in cache layer... oh yeah".
The "standard browser" is pretty much the most expansive standard library that exists.
To me the part that rubs the worst, is that _after_ downloading JS components, listening to events to build a local state, crafting the AJAX request, and rendering the return JSON as HTML, the SPA will then change the URL in the URL bar so that users can (hopefully) bookmark the monstrosity. This is after 75% of the page has been re-rendered anyway. And just to echo your point, there's an entire generation of frontend developers who think this is normal.
It's more like the other way around. Veteran web devs are stockholm-syndromed into thinking the web should always work how it used to work, they tend to think about the web as some sort of sacred internet document. Most devs these days view it as just another medium for apps, and if you look at the web from the app developer point of view, this is completely normal. Is it broken in many ways, absolutely? But not because it's not now the web used to work, as it was also broken/insufficient for modern needs.
There is an entire other discussion about how to keep the web as "information superhighway" (as opposed to an app medium) and accessible. I am sympathetic to that cause, but I think we should create new web standards for that. As it stands, the html/css/js trio is doing too many things and sucks at all of them.
I don't know about the kind of web apps you've been building, but most of the ones I've built over the last 10-15 years have had too much interactivity to be done with simpler server side rendering.
Everything from MPAs, to SPAs, to animation heavy websites that could only dream on being tailwind. Progressive enhancement doesn't mean giving up on frontend validation, dynamic functionality, and juice. I'm curious what problems you haven't been able to solve without converting whole applications to JSX.
My idea of a complicated UI challenge is not updating DOM in two trees. I have found React to be inadequate for the hardest UI challenges that I've been presented with. Things like DOM manipulation for interactive animations should not be handled by React state updates.
I think the SPA and the no-JS SSR people get it wrong by making it so black and white. By choosing to use something like React, you've made everything only SPA. With SSR, you can at least choose to progressively enhance the interactions. Look at amazon.com. They have plenty of full page reloads, yet their site also has tons of interactivity. Just make sure that the thing you require a full page reload for happens at a natural transition where it isn't interrupting the user's experience.
That's the exact opposite of my experience. Completely boring biz software that was, in essence, collections of forms. Didn't need the additional development burden of an extra JavaScript codebase, but that's what's in vogue, so ¯\_(ツ)_/¯
I notice you started by a blanket statment and use a strawman that is forms in React, a framework known for being the worst for forms (if you don't know the right library). Overall a poor criticism of SPA as a whole.
Having an opinion on best framework and libraries does not make what I'm saying straw man. React is by far the most popular framework for building SPAs and from what I've seen everyone uses a similar-enough state pattern to what has been described. I would love to hear your recommendations though.
> when you dont have to update the app in any other places when your state change
You can replace elements outside of your direct tree if you want. The simplest case you replace the whole page and pick what elements you actually want to change.
You're thinking about HTMX wrong. It's for progressive enhancement, the default is a full page reload and then you progressively enhance parts of the HTML. You should be using the same code paths and template to generate the islands of interactivity as you do for the whole page. You can then optionally send less HTML by just sending the parts that you expect to have actually changed, your "islands".
appreciate that. ive been given the progressive enhancement spiel a few times. its obviously a judgement call that will be the right call for some people. but i think many people underestimate how requirements grow over time because our UI standards have gone up over time, even for basic sites that you dont traditionally think of as SPAs. data drives everything, you want your UI to be a function of data.
for the other stuff, the growing browser stdlib has basically replaced most of jquery's usecases. so (in the most non condescending or negative way possible) htmx occupies a very awkward sliver between "The Platform" and Frameworkland and after giving it some time I have yet to see the benefit from keeping any of it in my head
I'm obviously in the minority, but I breathe a sigh of relief every time I encounter a website that works workout JS, don't hijack basic browser functionality, don't make dozens of AJAX requests in the background, and overall just focuses on presenting the data I want.
Every time web sites reimplement basic browser behavior, which they invariably do these days, they get worse, often significantly, because they never get it right.
> i think many people underestimate how requirements grow over time because our UI standards have gone up over time
I think you are overestimating how much 'UI standards' have gone up over time, actually. Nobody except for a small 'extremely online' set of people and control-freak designers care about all those complex UI/UX requirements. Users certainly don't care. They want fast-loading, responsive pages that get out of the way and don't drain their batteries or clog up their connection.
The other thing here is that approximately no one can actually tackle the complexity introduced by SPA frameworks and 'UI standards', and it's far more likely that they bungle it up and make giant, wasteful, UX pitfall-ridden apps. With server rendered pages, plain old HTML, and progressive enhancement, we at least have a better chance of producing something usable.
If you render HTML on the client you still have to render a transport format (usually JSON) on the server side. Sending HTML just lets you skip the transport format and the client side rendering.
And letting your framework do the bookkeeping for you lets you skip the whole issue. I don't know the exact specification of React Server Component Payload, but I know that it somehow changes stuff on the client-side when the client interacts with the server. (It sends something like `["$","p",null,{"children":["a child","another child"]}]`)
It's still one server side template, you just render different parts depending on what is calling it. By default render a full page, if you want to get precious about a few dozen KB from sending a full page over the wire you can toggle some blocks off.
> 1) you dont care about keeping UI state on the parts that are replaced (incl any stateful children of the UI element that happen to be there in your DOM structure), and 2) when you dont have to update the app in any other places when your state changes
Htmx does have tools for both of these cases. Out of the box, htmx throws out state, but there are plugins such as morphdom-swap for merging in the new DOM fragment into the old while keeping state. I have some client-only state that holds references to DOM elements in Javascript, and by default, yes, htmx breaks all those references as those elements no longer exist. Link in morphdom-swap, and my references live on across reloads, even across attribute and content changes.
And for #2, htmx also allows you to swap in elements that are not the target element, just by specifying that that's what you want.
IMO these are pretty basic tools of htmx. Like you said, without them about the most complex thing you can create is a to-do list, and sometimes not even that.
Parts of htmx were yoinked out of the core library into an extensions repo relatively recently, as part of htmx 2.0. That might explain the relatively lower number of stars. More important than github stars is that it is indeed part of the htmx project and is documented here https://htmx.org/extensions/
Just step back for a second and think about programming without modeling the states. Framework or not, no amount of hacking/tooling can help you with that.
I'm not super-stoked about the idea of building SPAs on top of htmx, but what I have found works incredibly well is to build a traditional MPA (SSR etc), embed islands of interactivity where needed, and where something fancier is really necessary, embed a React or Svelte app into just that one portion of a single page.
yea im not even talking about SPAs in my post, just the complexity explosion that comes with updating state in more than 1 place / preserving ui state in the place that gets rerendered. it blows up in your face quickly if you have even any requirement volatility (https://stackoverflow.blog/2020/02/20/requirements-volatilit...)
I agree that it becomes complex if you have state on the frontend. htmx scales better if you keep all or most of your state on the backend. I've found that using the websockets extension really helps with automatically keeping the frontend in sync.
> I've found that using the websockets extension really helps with automatically keeping the frontend in sync.
I choked reading this imagining people thinking they're doing something simple (as in not complex) by introducing websockets so they can keep state in their Go backend and sync it with their front-end, ya know, to keep track of the # of TODOs checked.
But it is simple :) The underlying technology might be more complex, but the library is solid, it's trivial to update any part of the page once you have the libraries set up, and you don't need to write any javascript. Works for me!
I think you may mean easy. It may be _easy_, but it's not simple. There are so many more moving pieces, failure modes, operational issues now to consider. Websocket connections aren't free.
I guess I just have to disagree. My experience is that it is robust, and removes an entire category of problems that appear when your state is spread across the back- and frontend.
As someone else mentioned, SSE is a somewhat simpler protocol that achieves the same purpose. Same idea though.
If you mean SSE, then yes that would work just as well (unless you need the bidirectionality for the client to modify some aspect of the connection after the page has loaded). There is an htmx-sse extension too.
I'm not sure how XHR alone would let you automatically get backend state changes reflected to the frontend. You can poll one or more endpoint, but that's more complicated to get right and less efficient.
there's nothing to sync, the state is only in the backend. if you tell the user that the TODO is checked and you're only keeping track of it in the frontend and you don't sync it to the server and it's lost in the meantime, your UI lied to the user when they checked it and it showed as checked. With state on the backend, the user doesn't see that their data is saved until, you know, it actually is. And if all the state is rendered from the backend it can't get out of sync with the display
I hate it when a UI tells me I did an action when really there's an asynchronous background task happening that may fail
htmx can work well for a reasonably large class of web applications. not all, and certainly not all UI patterns, but w/a bit of a mind shift to the hypermedia approach & a bit of accepting some limitations thereof you can simplify a lot of things pretty significantly.
if you try to shoehorn it in to the SPA mindset, triggering server requests on every interaction, yeah, it's not gonna be great
Glad to see you here swyx. I think the issue you outlined is acknowledged by the htmx's authors and addressed in the Morph Swaps documentation: https://htmx.org/docs/#morphing
What is ironic though, is that the morphing algorithms are similar to what modern SPA frameworks do, except that it can be addressed without introducing new concepts such as vdom, zones, signal etc.
I agree that the island architecture is limiting when it comes to large scale application with state shared across multiple areas in the UI. I'd be curious to hear others finding success in building apps.
> frontend frameworks will be right here when you get back from speedrunning the last 10 years
More like speedrunning the period between 20 years ago and 10 years ago! React is 11 years old, much older than jQuery was when React was initially released.
thanks. yeah i will be the first to admit i've only spent like 2 days with htmx so i wont know everything (but still...)
re: hx-preserve. what if i want to "conditionally preserve" - preserve this element when my state is one way, but not in other states? i dont see a way.
re: hx-swap-oob. looking at https://htmx.org/attributes/hx-swap-oob/ i think it still does not address what i'm looking for. any master-detail list kind of UI will want updates in 2-3 places when 1 piece of state updates (aka ui consistency, ui as a function of state). i fail to see how attaching an attribute with 1 place for an ID solves that. perhaps theres another api for "multiswap"? even if it existed... idk if i'd be comfortable using it man (ofc, i am clearly biased/taught to "think in components" from 7 years of react exp)
You're misunderstanding hx-swap-oob. Each element with that attribute will go and replace the element with the matching ID, keeping them all in sync with one response from the server.
'preserve this element when my state is one way, but not in other states'
You could send the state back to the server with the request and respond appropriately on the backend. Or check in the db without sending it, if it lives in the db (it probably should?).
Google for AHA stack. (Astro, HTMX, Alpine) There was a great site by Flavio Copes that went into a lot of detail on using them together but it looks like it’s gone.
It's pretty good combo. I use Alpinejs for the client side interactivty such as modals, but then use htmx for as much as I can that interacts with the backend.
You could be using only htmx or only alpine but the combo is really nice
In that case, IIUC, state is managed on the server, and the client is only responsible for rendering views generated on the backend and returning user input via form or json. This is what htmx was really designed for, anyway.
I am not surprised around (1). HTMX is based on REST, which is by nature based towards having the state in the server and having a stateless client. But what is the advantage of having the state in the client. Isn't that just a bias of the current frameworks?
I can see cases where you really need a thick client, and in that case I would use GraphQL rather than REST for those. But, there are quite a few cases that can work with REST and mostly server side.
I think (2) is solved by out of band capabilities of HTMX. I don't think that is naturally a limitation of the conceptualisation.
The problem is that you need to remember every single place in your app that needs to be updated when you change state instead of your framework doing it for you. You said it yourself-- the "out of band capabilities". Instead of your UI automatically updating in reaction to a state change, you have to manually write out every single component's markup while being extra careful to make sure that each ID matches. [1]
This is why HTMX is ultimately an imperative framework. You have to wire everything up manually yourself instead of simply building a declarative representation of your UI and letting your framework handle the synchronization.
Did you ever use jQuery? The negative aspects are remarkably similar to the point that it is mildly amusing.
Regarding state on the client, my general concern is connectivity with the server. If I have a flaky mobile connection, or if I have a really high RTT to the server for any reason, then I don't want UI interactions to start failing or slowing down left and right. Indeed, a lot of the sluggishness I've experienced with others' websites has to do with excessive roundtrips for trivial tasks like clicking around. So I'd prefer to keep most UI state in the client, if nothing else. (Of course it's still quite possible to make excessive roundtrips with an SPA framework, but it naively seems like it's at least more controllable than if everything is rendered server-side.)
people give me grief all the time because i don't use proper capitalization in my texts. they even ridicule me if i say "you understand what i mean so who cares"
but i don't get to do the same thing when they say stupid shit like "smol". that's just normal, while i am retarded
Using capitalisation is actually reasonably useful for people with certian classes of disabilities, maybe concider it purely from the perspective.
So is breaking up things with whitespace which you did reasonably well.
So it's not a matter of "You know what I mean, so who cares?". They may realistically have had to put in significantly more effort into understanding what was written.
That isn't entirely true for something silly like the word smol.
i often avoid capitals myself in informal writing, but to be fair, there's a valid argument that period is pretty small and can be hard to spot by itself for something as important as the statement separator . capital letters help with that somewhat, although it doesn't help that we've overloaded them for other things . putting a space before the period helps keep it more distinct, but i really wish our punctuation was better designed (right after we reform english spelling i guess ...) .
but i digress . whataboutism aside, what's the actual problem ?
I agree with you. After seeing some internal prototypes at my job abusing htmx boost.
I'm slowly going on the anti-htmx bandwagon.
I'm still not pro-react or pro-vue. We need a stabler frontend js framework.
It seems like it makes easy things easy and hard things hard, which seems like a pretty poor value proposition - at least relative to some of the hype around it.
I think the takeaway here is "make the easy things easy, don't use it for the hard things".
One of the most exhausting things about any discussion about front end web dev is that it gets treated like a monolith. It _can_ be incredibly complicated. In many scenarios that complication is unwarranted. But in some it's justified.
Htmx is a poor choice for a full single page app. That's fine. It excels in other areas. Right tool for the right job.
But easy things are also relatively easy when using other tools that scale up better. Even if HTMX makes the easy things slightly easier, is that worth investing time and energy into learning it in addition to those tools that scale up better? Is it worth building things with it if I know that months down the line I'm going to need to scrap it once my easy thing becomes slightly less easy?
Like I said, this seems like a poor value proposition to me. Of course, if other people are happy with it more power to 'em.
To me it feels like people are eager to latch onto this because it's different, and because there's a dopamine hit associated with seeing the easy thing become a little easier when taking a new path that's overoptimized for it - hence the hype - not because it's actually good engineering, in terms of the tradeoffs you're making.
Of course, I feel the same way about Tailwind, so maybe I'm just old and grumpy.
Because it's a drop-in, no-dependency, no build-step library that can make your static MPA a little less static for the little bits of interactivity you need with not much effort. It's what you reach for to avoid "write the whole website in JS."
React -> "The site exists in JS, HTML is just a render target."
jQuery -> "Poke at the HTML from JS." Which is brittle as hell.
htmx -> "The site exists in HTML, extend HTML to handle the common tasks you want to do with it."
> Even if HTMX makes the easy things slightly easier, is that worth investing time and energy into learning it in addition to those tools that scale up better?
Depends what your aim is. Do you want to become a full time front end engineer? Then no, probably focus on other frameworks. But do you, from time to time, want to put small pieces of interactivity on web pages when it isn’t the sole (or even major) focus of your job? Htmx might be ideal.
For me the strength of htmx is to allow non JS languages and frameworks on the server side. Now I can use Raku+Cro+Red to make a modern website with dynamical modern UX. The standard paradigm is keep all state in isolated htmx state machines (eg modelling a form submission) or real state stored in my db and this works fine for my simple site needs.
> strength of htmx is to allow non JS languages and frameworks on the server side
I don't see how that has anything to do with the choice of htmlx. You always have the freedom to use any languages in the backend. If anything, htmlx imposes a bunch of hypermedia convention on top of your conventional API, regardless of languages.
Back-end devs think htmlx will allow them to be comfy in the back-end and just trash their front end with "thin client" that has no framework or any sort of organization. Eventually someone who is willing to actually think about front-end has to rebuild it and decouple the 2. Happens all the time.
I build quite basic websites for money. Today I use WordPress and write some combination of PHP/JavaScript with Advanced Custom Fields where I need custom storage. So tbh anything else has gotta be better! This means I have both front end and back end aspects under my personal control.
I want to be able to be able to code in a more natural back end setting - I like Raku, others may prefer Go (or Rust or PHP or JS). I am not ready to commit to a complex event driven framework like using the WordPress backend and React on the front end. But I do recognise that a pure browser only model for UX is too crummy … let’s say I want to validate an email … well if I specify input email in HTML, my browser will pop and ugly dialog if there is no @ in the input.
So HTMX gives me my backend coding preference and a clean way to add simply tweaks to the UX. That is the power of HTMX for me.
I don’t think HTMX is a good solution is you want to write Google Maps or Facebook. But it is a great way for straightforward sites to be written and I do not understand why I will be forced to step into JS front end rewrites if I go this route (and I can happily bundle some JS + CSS + HTML into an HTMX response for the islets where I need it.
> I want to be able to be able to code in a more natural back end setting
I think the point the OP is making is that very new frontend frameworks dictate what backend language you use. They just use HTTP requests and consume JSON.
On the contrary, making easy things easy and hard things possible is about as good of a value proposition as I can realistically expect. Hard things are going to be hard no matter what, and making them easy usually either adds excessive complexity or else makes easy things hard (if not impossible).
Making easy things easy and hard things possible is great, I agree...but that's not what I wrote. And I don't think that's what HTMX provides - it mostly gets in your way with the hard things.
Making hard things hard is a subset of making hard things possible. Better than making hard things impossible, which is the usual result of making easy things easy :)
> And I don't think that's what HTMX provides - it mostly gets in your way with the hard things.
And yet they're still possible. Like I said: hard things are going to be hard no matter what. Even if the hard things are harder, that's still a worthwhile tradeoff for maximizing the easy things.
in other words, this todo app is just about the most complex of a frontend you can easily* build with htmx, i'm afraid. try to fix either 1 or 2 and you end up building components and your own little framework.
as an exercise to demonstrate, try taking OPs code and adding a count of todos on each All/Active/Completed tab that should update every time u add/edit/delete the todos. see how much extra ui code that takes. compare with equivalent [framework of choice] impl (in most it will just involve 1 state update, thats it). this is htmx's explosion of complexity that makes it not [ optimized for change ] (https://overreacted.io/optimized-for-change/). code that is hard to change eventually calcifies and consumes code that is easy to change if you do not consistently garbage collect (nobody does)
i bought the hype too until i tried building something nontrivial in htmx and im afraid the aforementioned islands of interactivity you can build are very very smol islands indeed.
happy to revisit my opinion if there are componentlike design patterns in htmx i am not aware of.
*emphasis on easily; with enough elbow grease u can do anything ofc. but then you fall out of htmx's very narrow [ pit of success ](obligatory codinghorror dot com link)