</> Htmx – The Fetch()ening
79 comments
·November 3, 2025booi
SquareWheel
It's an amusing solution, but if this ends up being anything like the missing PHP 6, it's also going to cause confusion for users. It might've been better to just mea culpa and release 3.0 anyway. I can't imagine anybody would really hold it against the author.
pavon
As long as they commit to making the next version after this htmx 8, I will be content with this versioning scheme.
recursivedoubts
if there is another version of htmx it will be 8 for sure
abnercoimbre
A mea culpa would've been just fine. Even Big Tech can't keep their promises around this stuff (Windows 10 was the last OS remember?)
johannes1234321
What's the confusion around PHP 6? - There would have been a lot more confusion due to articles, talk slides, even printed books, .. talking about PHP 6's Unicode support would have been truly confusing.
fmbb
Should go directly to htmx 4.1, so we can finally have xhtmx 1.0
tidbeck
Got a https://en.wikipedia.org/wiki/Leisure_Suit_Larry#Leisure_Sui... vibe from it.
Waterluvian
Spaceballs 3: The Search For Spaceballs 2: The Search For More Money
jraph
The author is lucky the phrasing wasn't "there won't be another major version of htmx", or even "a third version".
null
null
gr4vityWall
> htmx 2.0 (like htmx 1.0 & intercooler.js 1.0) will be supported in perpetuity, so there is absolutely no pressure to upgrade your application: if htmx 2.0 is satisfying your hypermedia needs, you can stick with it
This is commendable, specially during times when libraries and programs aren't afraid of breaking changes and API churn.
deepsun
> hx-target attribute is explicitly declared as inherited on the enclosing div and, if it wasn’t, the button elements would not inherit the target from it.
So confusing. I'm pretty sure it should be "inheritable", because "inherited" on an attribute means the attribute is inherited, not the element's children will inherit the attribute.
UPDATE: or "inherit", sounds like a command, little less confusing.
SoftTalker
I was going to make the same comment. "inherited" is the wrong word. I like "inherit" too. And it's a bit shorter which is always nice. I guess "bequeath" might work also?
recursivedoubts
yeah, i'm struggling w/it, willing to consider options
mrj
Maybe `heritable`?
jakubmazanec
> So, eventually & reluctantly, I have changed my mind: there will be another major version of htmx.
So first you weren't going to make a new major version, because htmx was sooo perfect, but now you had realized how much it can be improved.
Obviously, all software needs to evolve, and it was always very silly to say "this is the final major version". Why would someone use software from such kind of developer is beyond my understanding. But of course I also don't understand anything about this library; this surely must be some kind of trolling:
> We are going to adopt a new standard for event naming to make things even clearer:
> htmx:<phase>:<system>[:<optional-sub-action>]
It's truly wonderful what can people do to avoid writing JavaScript :D
patates
yes, you write this weird custom syntax that's interpreted in js, without writing any single line of js!
well, except when you want to do drag and drop sorting and this other thing.
yeah you get to communicate intent with html, but ignoring the security concerns for arguments sake, an inline script tag or your good old onclick event handler can do that too.
SoftTalker
Declarative style is almost always a win, IMO.
epolanski
The only trolling I see is in your tone.
Okay, the author changed idea, so?
What's your point?
atomicnumber3
it's truly wonderful how much can be done and still be better than writing javascript
blovescoffee
Do you actually feel like this is better (and not just at par or worse)?
jabbywocker
I find it much better for my use cases to use this than to use a JavaScript framework the necessitates the use of a JavaScript server
lazypenguin
> By switching to fetch(), we can take advantage of its support for readable streams, which allow for a stream of content to be swapped into the DOM, rather than a single response.
Based on this section, it will be interesting to see how this evolves. I've used HTMX a bunch but after stumbling on Datastar I've come to prefer it. Partially because I don't need something like alpine.js to get some frontend goodies but also because I've come to appreciate the power of SSE streaming morphable targets to the browser
nchmy
Great to see this evolution! Realtime ssr hypermedia is definitely the future.
But, my thoughts immediately go to Datastar, which has Fetch, SSE, declarative signals and js expressions, dom morphing, and much more - in a tiny package. I find it to have a more flexible, expressive and standards-compliant API as well. And it'll soon have a simple reactive web components and css framework as well.
At this point, why use HTMX when it really seems like (a heavier) Datastar-lite?
arcanemachiner
Because Datastar Pro isn't FOSS, and speaking from a purely probabilistic and historical standpoint, your odds of getting rugpulled (in some form or another) might as well be 100%.
nchmy
I can appreciate why people would have this perspective - I have been rugpulled a few times recently by thoroughly unscrupulous companies (Augment Code are trash humans).
But Datastar is different. The project is literally owned by a 501c3 non-profit. The devs have dayjobs and donate their spare time to this. Funds are for going to conferences or hosting their own
And 99% of the features/value that I mentioned is MIT licensed, and the "rugpulled" code is still available to easily port via the plugin API.
epolanski
You don't know that, even if they were your brothers sitting in the room with you right now you don't know if they are or not.
andersmurphy
Yeah HTMX should rebrand as DATASTAR LIBRE.
reverius42
Presumably Datastar is a trademark that shouldn't be used that way? (Or does the owner of HTMX also own Datastar?)
alexpetros
> why use HTMX when it really seems like (a heavier) Datastar-lite?
The reason to use htmx is that it has a simpler interface optimized for the majority use-case.
With htmx, you are largely tied to a request/reply paradigm. Something happens that triggers a request (e.g. user clicks a button, or some element scrolls into view), htmx sends the request, and then it processes the response. The htmx interface (`hx-get`, hx-trigger`) is optimized to make this paradigm extremely simple and concise to specify.
Datastar's focus (last I checked) is on decoupling these two things. Events may stream to the client at any time, regardless of whether or not they were triggered by a specific action on the client, and they get processed by Datastar and have some effect on the page. htmx has affordances for listening to events (SEE extension, new fetch support) and for placing items arbitrarily on the page (out-of-band swaps) but if your use-case is a video game or a dashboard or something else where the updates are frequently uncorrelated with user actions, Datastar makes a lot of sense. It's a bit like driving a manual transmission.
Delaney is fond of saying that there's no need for htmx when Datastar can technically do everything htmx can [0]. But I think this misses the point of what makes htmx so popular: most people's applications do fit within a largely request/reply paradigm, and using a library that assumes this paradigm is both simpler to implement and simpler to debug. As an htmx maintainer, I often encourage people to even use htmx less than they want to, because the request/reply paradigm is very powerful and the more you can adhere to browser's understanding of it, the more durable and maintainable your website will be [1].
[0] https://data-star.dev/essays/v1_and_beyond
[1] https://unplannedobsolescence.com/blog/less-htmx-is-more/
nchmy
Respectfully, almost everything you said is just plain wrong.
1. Datastar supports req/reply just fine - be it via normal text/html responses, or SSE (0, 1, or infinity responses) https://data-star.dev/reference/actions#response-handling. So, the crux of your argument is moot...
Moreover, if htmx's real value is ajax request/response, then why are you introducing SSE as a first-class citizen now?
2. Datastar has data-on, and various other attributes, that allow for triggering far more actions than just backend requests, from far more (any) events. I'm glad to see that htmx is now following suit with hx-on, even if it is apparently limited in capabilities.
3. Datastar can do OOB-swaps just fine - that's literally the core functionality, via (their own, faster) idiomorph.
4. Its a misnomer that Datastar is for video games etc - again, as described above, it can do all of the simple things that that HTMX can do, and more. And, again, why is HTMX introducing SSE if its so apparently unnecessary and unwieldy?
5. What makes htmx popular is that it was the first library to make declarative fragment swapping easy. And Carson is just a god-tier marketer. Its nice to see that he's now realized that Delaney was on to something when he wanted to introduce all of these v4 features to HTMX 3 years ago, but was (fortunately for us happy users) forced to go make Datastar instead.
6. We havent even talked about one of the key features - declarative signals. Signals are justifiably taking over all of the JS frameworks and there's even an active proposal to make them part of the web platform. D* makes them simpler to use than any of them, and in a tiny package.
I, Delaney, and all other D* users are grateful for HTMX opening this door. But I reiterate my original question - now that HTMX is becoming Datastar-lite, why not just use Datastar given that the powerful extras don't add any complexity and comes in a smaller package?
alexpetros
So here's the htmx example for click to edit: [0]
<button hx-get="/contact/1/edit">
And here's the datastar one, edited for parity: [1]
<button data-on:click="@get('/contact/1/edit')">
The htmx one is simpler. There's fewer mini-languages to learn and the API makes more assumptions about what you want. As you noted, Datastar has more generalized mechanisms that are certainly less clunky than htmx's if you lean heavily into more signals- or event-driven behavior, but for (what I believe to be) the majority use-case of a CRUD website, htmx's simpler interface is easier to implement and debug.(For example: you will see the response associated with the request in the browser network tab; I'm not sure if Datastar has a non-SSE mode to support that but it wouldn't be true for SSE.) To each their own.
As for "well then why implement X, Y, or Z," as the OP notes, refactoring to use fetch() means you get them largely for free, without compromising the nice interface. So why not?
andersmurphy
Why bother with v4 at all? If it dilutes that simpler interface?
I think that even with req/resp morph leads to a simpler majority use case and that's what Turbo and Datastar have both shown. No?
alexpetros
> Why bother with v4 at all? If it dilutes that simpler interface?
v4 makes almost no changes to the interface, other than to flip inheritance to be off by default.
> I think that even with req/resp morph leads to a simpler majority use case and that's what Turbo and Datastar have both shown. No?
Although you can use the idiomorph extension for htmx, I personally don't think idiomorph is simpler, because there's an algorithm choosing what parts of the page get replaced based on the server response; I prefer to specify exactly what parts of the page get replaced in much simpler terms, a CSS selector, with `hx-target`.
Per [1] above, my style is minimize partial page responses wherever possible, so the ones that I do have are bespoke and replace a specific thing.
andersmurphy
The irony that the Datastar author originally tried to have those features added to HTMX a few years ago.
nchmy
Better late than never. Everyone benefits with HTMX evolving and bringing more attention and capabilities to hypermedia-first approaches.
amanzi
One important difference that I found is that HTMX (and Alpine AJAX) can easily push a URL into the browser's location history. I've used this feature often with my Django projects--essentially storing the state in the URL which is great for sharing URLs or bookmarking them. As far as I'm aware, Datastar have locked this feature behind the "Pro" paywall.
nchmy
Fair. Though, D*'s authors are pretty adamant that this is an anti-pattern - that's why they put it behind the paywall.
Moreover, the FOSS code still exists and would take 2 minutes to update to the current plugin API (I have Datastar pro and the code is almost exactly the same)
https://github.com/starfederation/datastar/blob/v1.0.0-beta....
yawaramin
> D*'s authors are pretty adamant that this is an anti-pattern
The Datastar authors are wrong about this. History push is a very important part of the hypermedia-driven application approach. Because URLs are super important. And we want to make sure that the correct URL is shown for the currently-loaded view, and that the view is reproducible given the URL (as much as possible) so that bookmarking and copy-pasting to send URLs just works as expected.
A really nice article came out about this just recently: https://alfy.blog/2025/10/31/your-url-is-your-state.html
I also wrote a bit more about it here: https://dev.to/yawaramin/why-hx-boost-is-actually-the-most-i...
amanzi
I'm interested in why this would be an anti-pattern? What would the alternative be?
AtlasBarfed
Pay for the antipatterns?
I'm too tired to parse this logic, but I suspect it is a novel entry in techcorp doublespeak/dirty tricks.
fukka42
Check out window.history.pushState
simonw
I really enjoyed reading this - very clear technical writing and I learned quite a bit about htmx and their approach to API design generally from it.
logankeenan
This is great! I had to create an xhr-fetch-proxy to use fetch and htmx. This change should open up a lot of fun possibilities.
recursivedoubts
in 4.0 the request cycle is totally open: you can replace the fetch() implementation on a per-request basis
learned that trick from fixi.js
logankeenan
Yep, it's super nice. The Service Workers API also makes this really easy too. I experimented with compiling a Rust Axum server to WASM and then ran it in my service worker. Also, thanks for incorporating fetch into htmx!
I thought I'd include an example of replacing fetch for anyone that come across this.
const originalFetch = window.fetch;
window.fetch = function(url, options) {
if (url.includes('/api/user')) {
const mockUser = {id: 1, name: 'John Doe', email: 'john@example.com'};
return Promise.resolve(new Response(JSON.stringify(mockUser)));
}
return originalFetch(url, options);
};
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWork...recursivedoubts
in htmx 4 you are able to swap it on a per-trigger basis, so need to muck w/the global fetch function:
<button hx-get="/foo" hx-on:htmx:config:request="ctx.fetch = myCustomFetch">
Do It
</button>andersmurphy
Looking forward to porting my demos [1] from Datastar to HTMX once V4 is stable enough. Exciting stuff. Nice to see HTMX innovating again.
adamzwasserman
Interesting timing - I'm building genx.software which leans hard into implicit inheritance for the exact reason he is abandoning it. In my finance app, when the product owner changes their mind about decimal places for the 47th time (and it's always 47), the last thing I want is to hunt down every cell and add :inherited modifiers.
We treat everything as integers server-side (because floating point is the devil) and defer all formatting to the client. Implicit cascade means I can change fmt-x="currency:USD:decimals:2" in one place and watch it ripple down the entire table. It's 'maddening' the way CSS is maddening - which is to say, it becomes second nature after you stop fighting it.
That said, I fully understand the support ticket burden. Maybe the real lesson is: implicit inheritance is great when you control the domain (like 'format all money the same way'), but terrible when people want to do arbitrary things at arbitrary levels. I will bear that in mind as I complete genx.software
epolanski
The image breaks the website on mobile for me.
amanzi
This looks great, although the version 4 alpha doesn't appear to be available at the suggested URL: https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha/dist/htmx....
"Couldn't find the requested release version 4.0.0-alpha."
ian-g
It's named 4.0.0-alpha1. Just add the 1 and the link works
amanzi
Thanks. The docs need to be updated at https://four.htmx.org/
> So, eventually & reluctantly, I have changed my mind: there will be another major version of htmx.
> However, in order to keep my word that there will not be a htmx 3.0, the next release will instead be htmx 4.0.
technically correct.. the best kind of correct