13 ms·
A modest critique of Htmx
- dartos 2y agoI’m going full stupid into HTMX. I’m building a thing with HTMX and hyperscript. Very excited to run into these issues for myself.
- recursivedoubts 2y agoThese all look reasonable to me. I especially go back and forth on attribute inheritance (it can be disabled via the htmx.config.disableInheritance option) Three of the criticisms boil down to the fact that client-side state doesn't always play well w/htmx swaps (especially the simple ones) which is absolutely true. And events can get crazy. They are powerful, but crazy and at times hard to debug. Such is event-driven life. The one thing I don't agree with is the default queuing mode: it is not to cancel an existing request and replace it. Instead it is to keep the current request in flight and queue one and only one additional request. I'd need to sit down w/them to see if they were misinterpreting something, using the hx-sync attribute to implement the behavior they mention, or if there is a bug. I would also like to take this opportunity to market our mug for people who don't like htmx: https://swag.htmx.org/products/htmx-sucks-mug https://swag.htmx.org/products/htmx-sucks-mug
- nickpeterson 2y agoI’ll definitely pile on with the inheritance causing issues. It made me feel like unsetting them constantly defensively.
- recursivedoubts 2y agoYou can disable it globally, see htmx.config.disableInheritance: https://htmx.org/docs/#inheritance https://htmx.org/docs/#inheritance
- dullcrisp 2y agoBy queue only one additional request, do you mean cancel any existing queued request?
- recursivedoubts 2y agothe code is a little gnarly, but if you don't specify anything the default behavior is to keep the "last" event that comes in while a request is in flight: https://github.com/bigskysoftware/htmx/blob/1242977d11bebe56dfe612346d668b37d1a38a24/src/htmx.js#L4210 https://github.com/bigskysoftware/htmx/blob/1242977d11bebe56... and that dumps the existing request queue and puts request created by the last event in it by itself: https://github.com/bigskysoftware/htmx/blob/1242977d11bebe56dfe612346d668b37d1a38a24/src/htmx.js#L4226 https://github.com/bigskysoftware/htmx/blob/1242977d11bebe56... we don't cancel the current request or issue the next request until the current request finishes but there could be a bug in the code, for sure, it's pretty crazy
- dullcrisp 2y agoI thought they were complaining that any request is being cancelled by a subsequent one, since they wanted all the requests they made to go through (presumably the requests are altering state?) Probably I misunderstood what was meant by “losing work” though.
- recursivedoubts 2y agoyeah i don't know if I understand what they were saying either regardless if you have a lot of events flying around then updating the UI via hypermedia exchanges is going to be a bad idea, as I mention here: https://htmx.org/essays/when-to-use-hypermedia/#if-your-ui-state-is-updated-extremely-frequently https://htmx.org/essays/when-to-use-hypermedia/#if-your-ui-s...
- angra_mainyu 2y ago>https://github.com/bigskysoftware/htmx/blob/1242977d11bebe56dfe612346d668b37d1a38a24/src/htmx.js#L4210 https://github.com/bigskysoftware/htmx/blob/1242977d11bebe56... This seems to be a scenario where switch/case blocks could make the elif-trees a bit easier to read. Also, the code could use some care about not going so deep into the Vs: // request headers if (requestAttrValues.noHeaders) { // ignore all headers } else { for (const header in headers) { if (headers.hasOwnProperty(header)) { const headerValue = headers[header] safelySetHeaderValue(xhr, header, headerValue) } } } could just be: // request headers if (!requestAttrValues.noHeaders) { Object.keys(headers) .filter(hdr => headers.hasOwnProperty(hdr)) .forEach(hdr => safelySetHeaderValue(xhr, hdr, headers[hdr])); } Not even sure if that hasOwnProp check is needed, unless header keys are explicitly set to undef.
- Nathanael_M 2y agoMan, you’re everywhere. I have a Montana’s restaurant in my city and I’m scared to go in because of you.
- recursivedoubts 2y agoyou should be
- ksec 2y agoI just want to say thank you. Not only because of HTMX, but for being a model and showing what proper "Engineering" should be. Knowing trade - offs and accepting the fact no solution is perfect. Although that may be because you have a background of Industrial Engineering.
- Narhem 2y agoI know there are other libraries which offer similar features (alpine.js), but none are as simple and focused as HTMX. It seems like such an elegant solution I’m surprised more people haven’t started using it. It just works.
- heavensteeth 2y agoI cannot express the pleasure I felt seeing prices in my local currency automatically, without the Shopify "Zoinks! Looks like you're on our American store, would you like to change to our Polish store?" modal I've come to know and hate. Thanks.
- dzonga 2y agothe good thing about htmx / javascript or even a framework like Vue - is that the authors know the web browser is not a 'pure' platform as React people try pretend it to be. because of the event system on the web - things yeah are weird. And thanks for bringing intercooler.js / htmx as alternatives to a crazy world.
- orangetable99 2y agoyeah, inheritance enabled by default bit me in the ass more than once. With template engines you end up trying to debug some weird behavior and it takes some time for you to realize somewhere up in the tree on a different file there's an hx-* tag being inherited. I should have disabled it early in the project, too late now. I also still haven't figured out how to properly use the "save history to local storage" thing. Often there has been a server state change between the user navigating away and clicking the back button and I see no option other than disabling the thing altogether.
- ricardobeat 2y ago> Even morphdom overwrites some things you’d expect it not to, so we had to patch it to avoid messing with input elements, and details elements. Would love to hear more about this issue. Preserving the state of elements like input or detail is the main function of libraries like morphdom, something must have gone wrong there.
- recursivedoubts 2y agoat the end of the day morphdom (and my own algorithm, idiomorph) can only do so much, because the current DOM APIs nuke much of the state of nodes when they disconnect from the DOM if you look at the algorithms they give up very quickly and simply merge the new nodes in once matching doesn't work at a certain level because of this there is a new API coming down the pipe, moveBefore(), that will allow much better morphing/preservation in general: https://github.com/noamr/dom/blob/spm-explainer/moveBefore-explainer.md https://github.com/noamr/dom/blob/spm-explainer/moveBefore-e... htmx supports it today if you enable it in chrome canary: https://htmx.org/examples/move-before/ https://htmx.org/examples/move-before/
- jasoncartwright 2y agoAfter hearing about it for years, and it apparently being popular in the Django community, I tried htmx for the first time just this morning on a very simple task in an admin interface. It's easy to put unobtrusively in place, fun to play with, and it worked. Reminded me of using jQuery for the first time. Would I use it for a complex and involved project? Initial impressions suggest probably not.
- candiddevmike 2y agoIt's jQuery but for HTML. Same spaghetti result, different language.
- recursivedoubts 2y agomaybe so, but sometimes spaghetti is delicious: https://htmx.org/essays/a-real-world-react-to-htmx-port/ https://htmx.org/essays/a-real-world-react-to-htmx-port/
- lucis 2y agoWe have been using HTMX to create performant storefronts and the results are satisfactory. https://farmrio.deco.site/ https://farmrio.deco.site/ is one of the largest clothing retailers in Brazil and all the frontend interactions use HTMX, alongside a partial rendering strategy we developed. More info at https://deco.cx/en/blog/htmx-first-class-support https://deco.cx/en/blog/htmx-first-class-support
- lelandfe 2y agoLove how good the CLS is. I wonder if you can deal with flashes of white during navigation with the new View Transitions API
- lelandfe 2y agoOh, it's because the site does `*{visibility:hidden}` during loading. Don't do that, show us the intermediate state >:) You're artificially making your FCP also be your TTI, which means page navigation, when everything should be cached and fast, feels slow. That's not something e.g. Lighthouse tells you. I recommend showing the page right away, even if there's going to be jank. Jank/Cumulative layout shifts can be fixed later.
- boredtofears 2y agoI'm not sure this is htmx's fault but I wouldn't exactly describe that as snappy
- heavensteeth 2y agoI thought that too, but whole page loads are slow too; I assume it's just because I live nowhere near Brazil.
- s6af7ygt 2y agoThe example on the blog post is one of those that makes me severely question HTMX and the stuff that we're doing. Doing HTTP requests to increase a counter, or affecting any local-only state change at all, seems so wild to me.
- yawaramin 2y ago> React and Htmx do not interact nicely. I want to dig into this a bit. React of course maintains its own rendered part of the DOM and htmx trying to reach into and change any part of that DOM is not going to go over well. It's just going to be replaced with React's rendering again on the next render cycle. htmx provides two points at which it can interact with other libraries or frameworks: DOM events and CSS classes. I don't see any problem with classes, but React's synthetic events would probably not work well with htmx (or any other non-React library really). Maybe frameworks like Preact which use real DOM events would work better.
- dsego 2y ago> Htmx-in-React?: The server sends a JSON blob that React converts into virtual DOM components. The author should try inertia.js, it has server-side routing and react templates.
- deleted 2y ago[deleted]
- sauercrowd 2y ago> The default queuing mode is bonkers > By default, Htmx will cancel requests that are in-flight if you trigger another request on the same queue (element). This seems like the only default that's reasonable to me don't know if the author has a specific example in mind, but if a user submits an input, changes that input mid-request and submits again the request needs to be cancelled, otherwise the UI will be inconsistent by showing a response to an outdated input Just processing one response after the other won't be possible if a response swaps out content with different IDs, so a second response won't be able to be swapped out in the same way
- naasking 2y ago> don't know if the author has a specific example in mind, but if a user submits an input, changes that input mid-request and submits again the request needs to be cancelled, otherwise the UI will be inconsistent by showing a response to an outdated input If you queue them and process responses in order, that would also be correct. Probably has fewer failure modes. Some indicator that an element is still being processed would help to prevent user confusion though.
- sauercrowd 2y agoThis won't work if a response is meant to swap out a div, the first response completing will swap it out - if this response isnt compatible with the same selector (e.g. a #response is not available) a second completing response won't be able to update the UI
- naasking 2y agoThere are many choices here, such as never setting outerHTML but only innerHTML/nested content. Also, "processing responses in order" doesn't necessarily mean "immediately mutate DOM for each response", eg. defer if there are in-flight requests and aggregate changes. htmx arguably gives you too much flexibility which is why there are no straight answers that work for all scenarios.
- wetpaws 2y agoAs a former PHP dev it's fun to see people reinventing the same wheels and hitting the same pitfalls industry solved 2 decades ago.
- hahahacorn 2y agoIs there an equivalent to Turbo Mount for Htmx? https://evilmartians.com/chronicles/the-art-of-turbo-mount-hotwire-meets-modern-js-frameworks https://evilmartians.com/chronicles/the-art-of-turbo-mount-h... I think it's one of the best ways to use Hotwire/Turbo. Default to Hotwire to whatever degree of understanding you have, and as soon as it feels like HTMX/Hotwire isn't the right tool, you can easily switch to your framework of choice.
- sauercrowd 2y agoVery much agree with you on this - htmx (turbo, hotwire, alpine,...) are not trying to replace all your client side logic. It's a spectrum of interactivity, and we should try to pick the best (whatever that may mean) tool possible for the job - sometimes that's hx-boosted links, sometimes it's a react app
- pier25 2y agowhat about using web components?
- stavros 2y ago> An attractive future direction might be to re-implement Htmx in React: The server sends a JSON blob that React converts into virtual DOM components. Isn't this exactly what HTMX was replacing? The client needing to have a ton of logic and state?
- klysm 2y agoTurns out there’s good reasons to do that
- nsonha 2y agoOnly veteran web devs are able to come up with such a bonker idea that not managing state explicitly is somehow better. That's just basic programming but I guess they don't need that to write html.
- NoGravitas 2y agoYeah, a lot of the article was reasonable, talking about implementation details they don't like, but once they started talking about problems with local state I feel like they went off the rails. The server needs to be the source of truth for application state, with the application pushing state changes through POST/PUT/DELETE requests.
- stavros 2y agoI agree, no matter how much we don't like reloads, the fact that they reset your whole client state makes things much simpler.
- Havoc 2y agoNot on this in particular but in general: I’ve held off on diving into front end because it’s just such a circus. So many options, so many opinions, so much criticism and then as if that wasn’t enough the whole fkin meta changes monthly. We’re doing react. No actual wasm. Wait no static page. Or maybe htmx. Or vanilla js. Or maybe a mix…well call it remix…it just never ends I do believe everyone involved means well and aims for technically strong outputs but my good the end result is still fuckin chaos. Backend and systems programming has people with strongly held opinions and flame wars too but somehow it feels more like a war between gentlemen and less The Purge chaos.
- recursivedoubts 2y agoi don't know really what to do about it, but i did write a book on the ideas of hypermedia and how htmx extends them that you can read online for free here: https://hypermedia.systems https://hypermedia.systems i think the ideas of hypermedia are fairly stable and relevant, particularly the concept of a uniform interface, even if htmx is just one implementation of them
- diggan 2y ago> then as if that wasn’t enough the whole fkin meta changes monthly. We’re doing react. No actual wasm. Wait no static page. Or maybe htmx. Or vanilla js. Or maybe a mix…well call it remix…it just never ends Who told you to do all those changes? Maybe change who you're listening to, or even better: don't blindly follow what others say/talk about, but listen and think about it thoughtfully instead. Stay on whatever framework/library/platform you want and feel works best for you and your use cases. No one is forcing you to chase the latest trends.
- jfengel 2y agoTrue, but it does suck to have backed the wrong horse. You framework/library/platform can rot if nobody else is using it. You can't get away with just ignoring it. It helps to pick "boring tech", and hope that what has worked will continue to work. But it's still unpleasant to hear about the X killer every week, when X is your bread and butter.
- dfabulich 2y agoThis "HTMX in React" idea just reinvented React Server Components. https://react.dev/reference/rsc/server-components https://react.dev/reference/rsc/server-components An attractive future direction might be to re-implement Htmx in React: • The server sends a JSON blob that React converts into virtual DOM components. • That would solve the component state problem. • It would mean we require no special bridge to use React components. • It would let us use our React-connected web fetching library, and carefully avoid the queuing choices made by Htmx. • It would solve the morphdom problems and browser DOM input elements problem, too, which is pretty much a solved problem in React. In this way, we could drop the Htmx dependency but retain the benefits of the idea. That is, given a budget to embark on such a big piece of work. RSC is still experimental, and the default implementation assumes that you're going to run JS on the server, which undermines some of the point of HTMX. But someday (likely in the next year or so) the RSC wire format will be standardized, and then any language can generate RSC JSON for React to consume.
- andrewmcwatters 2y agoTotally disagree with state in the DOM. It’s natively where it exists.
- steve_adams_86 2y agoAlso in the URL. Both are totally okay, and even desirable. We’ve become so accustomed to managing state in abstractions of the DOM that this seems like a crazy idea, but it has led to all kinds of pain points from complexity to, well, the DOM and URL not always reflecting the state of the application accurately. It’s pretty awful. The better we can keep state out of abstractions without losing maintainability or performance, the better.
- imtringued 2y ago<input type="text" name="contracts.0.expenses.priority.A.employees.0.annualSalary"/> What an amazing way of handling state! Or the part where you use data tags for something that should be done with JSON...
- naasking 2y agoWhy "should" it be done with JSON?
- rrgok 2y agoWhat are the other ways? XML inside the attribute value or splitting up in a bunch of data attributes?
- naasking 2y agoWell it's hard to say because the OP didn't actually specify what state he's storing with JSON. Given we're talking about HTMX, all durable state should be server-side and client-side state should be generated by the server to round-trip back to the server, so it's not really clear why JSON should should be used here. Typically in this context, state is stored server-side, or client-side in input fields or in the URL as parameters (HATEOAS).
- manchmalscott 2y agoThat last point about dropping HTMX as a dependency but keeping the benefits is why I personally find Phoenix liveview so compelling.
- ipnon 2y agoYes, I'm all in on Phoenix and LiveView because of this.
- dartos 2y agoI really like elixir, but I don't think i like live view. Handling events by writing functions that need to match unchecked strings feels _super_ clumsy to me. I know elixir processes are very very cheap, but I still don't like the idea of maintaining a stateful connection for each live view page, especially with the lack of typing. Jumping between my markup and the 10s of different handlers in my live view is annoying too. I like the locality of behavior principal a lot. Again, I really like elixir, but untyped languages kind of suck for dealing with the string based world of web frontends. That's why so many javascript shops take on the complexity of typescript.
- manchmalscott 2y agoGiven that the event names are passed as attributes on the html elements themselves, they’re pretty limited to being strings. I don’t know how else it would be done.
- dartos 2y agoI don’t know how either, but it still feels clumsy.
- deleted 2y ago[deleted]
- deleted 2y ago[deleted]
- JodieBenitez 2y agoComments about HTMX vs. React keep popping up, but I think they miss the point and it's probably a "marketing" problem on HTMX side if people feel the need to write these. On the contrary, Unpoly -which shares similarities with HTMX but is probably more high level- makes it very clear on its front page: Progressive enhancement for HTML Get powerful new HTML attributes to build dynamic UI on the server. Works with any language. Gracefully degrades without JavaScript. Granted, HTMX has "high power tools for HTML" right on its front page, but it also has "reduced code base sizes by 67% when compared with react". Anyway, as always, tools ad trade-offs...
- nprateem 2y agoFor state, just create a container with x-data and use alpine, then let htmx replace the content. Works pretty well, but you can end up with needing some events which are a mess to keep track of. No use if you want react though...
- begoon 2y agoHas anyone used htmx for a more or less large project? Everything turns into spaghetti code really quickly. Separation of API and frontend isn’t just for fun. It’s for making testing bearable. Finally, mixing data modelling (database) with presentation (html output) in one place (htmx handler) goes out hand really quick too, making testing much harder. But real coding cowboys from the golden age don’t write tests, right? :-)
- NoGravitas 2y agoDepending on what you mean by large, I have. Testing is just the same as on any server-side MVC application. And I don't get spaghetti, because the structure is provided by the backend framework. Same with the data modelling - there's a model layer in the backend project that can be tested independently of everything else. I can only guess that you're trying to put all your logic and structure on the front-end, with the backend only serving what the front-end needs ad-hoc? Don't do that. Build a classic MVC app (Django, Rails, ASP.NET MVC), factor your views into small, reusable components, and use HTMX to replace page elements at that level.
- hu3 2y agoI have, using htmx before htmx existed. Similar concept and functionality. jQuery plugin attach events like "x-post" and other attributes, sends data to server which always returns HTML. 800+ CRUD pages full of business logic, around 2k routes, single PHP server, single MySQL server, serves up to 3k requests per second seasonally, P95 around 50ms. Team still adding and changing features every 2 weeks, even after years in production. Stack is custom PHP framework, MySQL, custom jQuery plugin that acts similar to htmx. Onboarding is dead easy. It was made with a no-build frontend stack. Meaning there's no build pipeline to understand and fight against. I look at React/SPA misuses and self inflicted pain, and feel sorry for them.
- Daril 2y agoBefore HTMX I used Turbo Hotwired and Unpoly. From my very personal and pragmatic point of view (I am a lone developer : I need to create software solutions that are robust and that simply work without the need to spend months to learn a new technology, that probably will be replaced by a new one in two months or so, that no customer is willing to pay), HTMX is the most pragmatic and useful solution. It is complete in features and you can do literally anything with it and is easy to integrate in existing web applications. I used it with CodeIgniter and now with Golang.
- wordofx 2y agoReading the complaints about state makes me think the author has never built websites prior to things like react existing. The examples don’t even make sense, it just sounds like someone trying to use htmx like it’s react and then getting upset cos it doesn’t work how he expect it to. Lack of experience.
- can3p 2y agoLocal state is indeed a problem that's exacerbated by swapping logic. Simple example: you have a form + a collapsible block inside or maybe a dynamic set of inputs (imagine you're adding items to a catalog and you want to allow to submit more than one). If you save the form and simply swap the html the block state will be reset. Ofc you could set the toggle state somewhere to pass it along to the backend, but that's a pain already compared to spa approach where you don't even bother with that since no state is reset. You could you query string as mentioned in the article, but that's not really convenient when done in a custom way for every single case. Having said that I think that a way to go could be a community effort to settle on the ways to handle different ui patterns and interactions with some bigger project to serve as a testing ground. And that includes backend part too, we can look at what rails does with turbo My opinion is that htmx (and similar) approach is different enough to potentially require a different set of ui interactions and it usually hurts when one tries to apply react friendly interactions to it.
- chromanoid 2y ago"HTMX in React" seems to be a very complicated idea. Seems to me like Vaadin territory in terms of complexity. I think you have to embrace the MPA approach otherwise you will not be happy with any MPA technology.
- reidjs 2y agoI set up HTMX for a toy project recently, seemed fine to me. I think the philosophy of HTMX is the real selling point, sending structured content instead of passing JSON around. There is an opportunity here to either improve HTMX or build a competitor.
- chromanoid 2y agoFor the record with https://hotwired.dev https://hotwired.dev there is already a rather successful "competitor".
- rsyring 2y agoFor a more batteries included option, see https://unpoly.com/ https://unpoly.com/
- WesolyKubeczek 2y agoSuperficially, the cure the article is proposing smells way worse than the poison: implement htmx in React. I don’t know, maybe it really solves their problem, but it just feels so… wrong. I don’t know. (Shakes head, gesticulates, exits the scene)
- anentropic 2y ago> An attractive future direction might be to re-implement Htmx in React: > The server sends a JSON blob that React converts into virtual DOM components. I don't understand what is left of HTMX if you do this. Isn't that just React?
- teqsun 2y agoI don't have anything personally against htmx, but the general sentiment I've seen tossed around about it has led me to believe that some real hellmesses are going to be calcified in it by "IDGAF about the FE" devs which will inevitably be foisted upon some poor intern to "improve".
- novoreorx 2y agoThe "HTMX in React" idea seems like the author intended to offer some insightful thoughts after criticizing HTMX extensively. However, it comes across as somewhat amateurish and appears to be created mainly to demonstrate an objective attitude.
- rsrsrs86 2y agoC’mon, you might like it or not, but HTMX is a breeze to play with.
- deleted 2y ago[deleted]
- fijiaarone 2y agoI think the argument of HTMX is that if you have a simple UI, there is a simpler way of doing things. The corollary argument is -- Why don't you have a simple UI?