Let's make the worst Htmx
zserge.com
zserge.com
We recently rewrote a half-million LOC codebase from react to datastar, with a detour through htmx first, and the results are staggering.
First page load is 20KB down from a 750KB js bundle. 1 network request vs 40+. Total load time 0.1 seconds down from 2 seconds of spinners. Page refresh is so fast, the browser doesn’t even flash - there’s no massive js bundle to parse, so it can start rendering instantly.
But the most staggering result is that in-document navigation over SSE achieves up to 5000:1 compression ratio for network requests within the Brotli compression window. Yes, 5000:1, you read that right. When changing server state we rerender the entire page, like an immediate-mode game engine, and push it to the client over an SSE “fat morph”, in about 100 bytes.
It’s so fast and so much more stable, our users are literally having fun. With a medical device.
And this ends up ~50% less code to maintain for a VASTLY superior UIX.
You are really missing out if you don’t learn how this works. It can give you a significant competitive edge if you care enough to try.
Edit: Big BIG thanks to bigskysoftware and the datastar team for making the web a sane place again. You really deserve all the props.
Once you want realtime/collaborative app SSE + morph lets you do that easily. Add streaming compression and this approach is suddenly crazy bandwidth efficient.
For people coming from react this gives you v = f(state) over the wire.
Lastly signals handle the edge cases where an out of bound morph would nuke your ephemeral client side state (mostly text input).
You can do this in htmx but you are fighting the design the whole way. Turbo is a bit better but doesn't have signals.
[1] https://dev.37signals.com/a-happier-happy-path-in-turbo-with...
We made a lot of progress with htmx, but eventually hit a complexity wall where we were spending more time fighting the tool than solving the business need. Note, I'm not blaming htmx here, the documentation is clear about its strengths and limitations, and the hypermedia approach is still fundamentally right. But even with alpinejs in the mix, we just weren't able to achieve the smooth developer experience we were looking for.
So we tried datastar, and it basically addressed all the friction points we had:
- No more DOM hunting to figure out why the hx-get ended up in the wrong spot or why OOB swap is glitching out. D* morph just works.
- No need for hidden form <input> to submit client state, D* signals are cleaner and easier to use.
- No more micro-routes for targeted element updates to avoid hx-swap butchering page state. One route per page, just render the whole thing and morph takes care of the rest. Immediate-mode rendering and fat morphs removed ~50% of our routing table.
- SSE + fat morph is a superpower. This is view = f(state) done right.
We've had very few issues, D* just works and gets out your way. The team has really created something quite exceptional.The great news is htmx4 is adopting many of these ideas, so I recommend trying both and seeing what fits you best.
Makes multiplayer out of the box over large datasets trivial. See these demos if you don't believe me [1][2] (open them in multiple tabs and have fun)
One of the cool things is how extensible d* is. Everything is a plugin, and you are free to extend it to suite your needs. We've invested a few days building a few specific data-* attributes for our application and it's super convenient.
It took me a minute to change my mindset to Datastar’s way of doing things. But it’s been amazing. Firstly to keep 99% of the state and logic on the back end is just so nice - no more having to deal with two sources of state at the same time (front end and back end). Secondly the ability to regenerate the entire view from scratch anytime something changes and then have Datastar efficiently morph just the changes is just such a simple mental model. Every fiber in me was like “no way that can work, the cpu, bandwidth, latency etc” - but it is buttery smooth.
Basically, if your html fat morph frames are 65kb uncompressed and you make a small change like checkbox it will send 13bytes so that's 5000:1. Caveat is as long as your compression context window is big enough.
Disclaimer: I built Hyperspan :)
1) I want to take a JSON response, create HTML from it, and replace the target element with the generated HTML.
2) Similar to the above, take a JSON response, perform some action, and do nothing to the source element.
Is there a plugin to add this? I want to move away from jQuery, and having a small framework to wire up hooks would be useful.
(1) the exception is I think in one place concerned a flow for uploading images, where client side requests a signed url, uploads to that url, then registers completion. Getting the signed url back is JSON meant for the uploader
But now I want to add a JSON API to the program: I could reuse the same routes (/create, etc), but require an application+json header; or add a full /api/ component essentially duplicating the logic from the main app.
Neither option is great.
As a result for POST requests you have to have htmx routes that accept urlencode and API routes that accept JSON, and JSON is far superior. But for outputs I actually think separating your UI and API is helpful. The code reuse is not worth the entanglement of concerns.
That's what we did and it works pretty well in practice.
I'd assume because of the conception that json = data. Html = presentation. But if you just use barebones html, this could work very well. Just add a wrapper in the api to return json as html to try it out a bit.
A simple example is adding a note to a product or order. When you submit it, it gets inlined, and say a visual note counter is incremented. Usually I'll return multiple HTML fragments if it's convenient, but I'll return a data structure if needed.
They were making a big deal with this change. In the end the change is rather minimal. The outcome is reduced javascript code and more server side html templates. Pretty much most of the code is now handled on the server side. Just organise the html templates!
Pseudo example
Instead of :-
<code>
// returning as Json
[Get]
Response GetUser(int id) {
var user = getUser(id);
return ToJson(user);
}</code>
You are just doing :-
<code>
// returning as HTML
[Get]
Response GetUser(int id) {
var user = getUser(id);
return View("som-user-template", user);
}</code>
I don't know what 'hooks' you need, but whatever they are doing you can move them over to returning/updating section of html on the screen with htmx.
For example, on button click to GET html. Again, you can specify what you want to do with the returned html with attributes such as hx-swap
Unless I am misunderstanding, of course.
I do remember triggers/hooks with things like Angular and Knockout. I don't miss this method of web development.
I recommend starting small and working up with htmx. Their examples section on the official website is pretty good.
Of course, there are all sorts of edge cases and minor features to add to make it complete.
People throw salt at my stack but it’s fast, and straightforward to manage attack surface
I wish I did more front-end stuff so I could play with it more.
cheers
For areas that need server side, if you use say react and return a JSON you have to deserialize and do the render. It might be much more efficient to just replace.
Having said that I use HTMX /alpinejs for small projects, and still trust react for bigger more interactive ones.
It is quite pleasant to work with, because you don't have to worry about a constant stream of vulnerabilities or supply chain attacks. You just vendornthe script in your repo, add it as a <script> tag on the page, and you're done.
Your mileage may vary, but I sleep better with my own projects not having any node_modules in their build tree.
Radio waves caught up in developed countries with 4G roll outs in my opinion(is that mid 2010’s?).
Large client side JS has been garbage for most things since it’s inception.
Htmx is a replacement for network interactions, not every bit of UI interactivity.
My favourite thing about TSX is that it is typechecked. How do you do that with random strings of HTML?
great idea thank you for the recommendation
But then I'd ask my original question that I didn't post: how the hell does this repo have 422 contributors?
GitHub's contributor graph is based on authorship of Git commits, and doesn't track whether a given GitHub account ever actually interacted with a given GitHub repo. This Git repo is a fork of the htmx Git repo (though it's not marked as such on GitHub), so all contributors to htmx as of the fork date are also contributors here.
Thanks for engaging me and my confusion.
It loses things like scroll position and text highlighting on re-render - it’s not high performance like React.
This is for server sided or static web pages not building rich UI.
There is no robust state management or DOM performance improvements you get with React with larger components.
HTMX team should build a game with it. Load some 3D in WebGL/WebGPU, showcase high performance UI.
Or showcase complex state management.
I need more than syntax idealism.
Why? Why you'd pick htmx (or even reactjs?!?) to build a game?
react-three-fiber solves a lot of problems. React state is also perfectly in line with game loop architecture, r3f unifying the Three.js/React render loop is incredibly good for game dev.
But even for SaaS or whatever, I need to know that it can handle complex states of the UI - HTMX can't.
They are overselling it. It's not really a competitor to React, it's more like a competitor to Jade templating or HAML which nobody already uses anymore.
And no way react is going to be faster for the above use case.
You miss the point of tools like htmx/datastar/turbo/unpoly/alpine, they are not a react competitor in the rich and complex web apps space. They fill a gap between the native browser capabilities and tools like react/vue/svelte/ecc.
It's simple enough that you don't need training or lengthy guides about "the rules of books etc".
It's small enough that an LLM can store the entire thing in context and answer your questions.
And it doesn't npm or a build step.
Examples include user highlighting text - swap wipes that out. Or user mid scroll through a menu, the scroll is reset to the top.
It's not enough in HTMX to just break it down into smaller components, the scrolling part is native to the browser, so is text selection.
Some people forget or don't know how much an SPA library like React is doing to prepare the SPA before you get into any of the organizational framework usage. Beyond that, clever engineering went into React DOM reconciliation in particular that I rarely see challenged in other libraries.
On game dev: I think it's good for a showcase because it shows the limits of what it could do at 60+ fps in a performance intensive environment. But yeah, even a complex SaaS demo would do.
> They fill a gap between the native browser capabilities and tools like react/vue/svelte/ecc.
Have not heard that yet, since a big part of HTMX is manipulating the DOM and binding events. I'm pretty sure this is not correct
Did you ever open the htmx home page? Because it's at the very top in the "motivation" section!
I'm starting to wonder whether you're trolling or you have no idea what HTMX and similar tools actually do.
"HTMX and React represent fundamentally different web development architectures, but they can be compared or even used together in a hybrid setup."
I see tutorials on how you can mix it with React for Next.js which might make sense at the page level because it's SSR.
Definitely possible, sure. But it would be about like mixing Angular with React where two libraries are competing for the truth of what's in the DOM.
There was a time Three.js "couldn't work" with React because they each had their own separate render lifecycles - but r3f happened with enough demand (and useFrame bridged the two beautifully).
Maybe the same will be true of HTMX, maybe people are actually trying to do that now for some reason, but yeah a lot would have to change with either React or HTMX to get value out of both simultaneously for UI.
I 100% disagree with this comment.
Lets make this clear, htmx does not swap 'entire html' - you have control to refresh whatever section of the page. That is not a full repaint.
I am beginning to wonder what tests you have done in htmx before making such a verdict.
Yes - I break my Views into "small components" - I guess it's better to say I create a lot of Partial Views. I follow a simple 'where there's one, there's many' mindset. All Partial Views are designed to return an array, even if what I need is a single record.
> Examples include user highlighting text
I've written an Application (in htmx) that has a text area for sentences. There is 'compile' function that breaks down the sentence, colouring specific words/phrases. How does it do this? htmx! It sends it back to the server-side and builds the sentence.. returning back the html with coloured texts and other meaningful attributes. It overwrites that section of the screen. It is light and fast!
The great thing is all validation and html rendering is done of the server-side. No code duplications or anything.
Now, this is perfectly fine for what I am doing. However, the text is barely no more than 30 words. If I was trying to write a text editor the we have a different type of problem. If using htmx, would likely work it out to update a portion of the editing code, perhaps per line. This would move it away from using a text area.
> Or user mid scroll through a menu, the scroll is reset to the top
I believe, with the text solution I mentioned above, I also pass it the cursor position. As we are processing the text, any changes may move the cursor - so I let the server-side handle it. This is great because the functionality written is not designed for just the web - but any 'text area' in any GUI!
From memory, with some javascript, I managed to return to the caret position once the html section was refreshed. I cannot remember 100% how I did this as I do not have access to the source code.
> On game dev: I think it's good...
I've already commented about game dev and htmx. I will paste that section here:-
Would I use htmx if writing a web-based game? probably not. I guess it would depend. I would assume I'd be writing a fair be of javascript for WebGL-based rendering and typical update logic (input, physics, health, etc)
Maybe WASM is a better choice in this domain. I don't know.
They quite literally have a a large essay on their site dedicated to discussing when to and when not to use it: https://htmx.org/essays/when-to-use-hypermedia/
Also, it’s not at all sold as a ‘competitor to React’, it’s sold as a simpler option for many apps where React is overkill. If you want to make a game with React, no one will argue for using htmx instead. The game will probably have a lot of perf issues though.
- Hm, maybe I should use react for this
- ok, why react?
- because the news of frontend's death has been overstated
???
- Because someone told me frontend was dead.
Go back to the grandparent comment, it doesn't ask why you'd choose a backend tech, the whole subthread is about frontend tech.
Every website you create, just like any form of application, is based on solving a particular problem. How you solve that problem can vary from dev to dev. Some devs may disagree you your solution just as you can disagree with theirs.
I do stand that htmx can be used to solve a good chunk of problems that otherwise would be done with React, or angular, and so on. Personally, I think htmx makes the problem easier to solve. Well, once you get over the learning curve of the htmx way.
To be able to build a website or SPA without writing much client code (JS, etc) is a win in my book. You don't need a dedicated front end team for larger sites. Also, the designer/UX team (if you have one) can focus on the server-side template system.
Regardless - frontend is not dead. htmx, afterwall, is written in Javascript. htmx doesn't stop you writing javascript code. It's just I hardly need it for business logic. If anything it might be used to compliment CSS.
Would I use htmx if writing a web-based game? probably not. I guess it would depend. I would assume I'd be writing a fair be of javascript for WebGL-based rendering and typical update logic (input, physics, health, etc)
Maybe WASM is a better choice in this domain. I don't know.