HTMX gets a lot of attention, often reduced to "the thing that avoids React". That misses the point. Let's go back to its origin, its idea, what it really changes — and what v4 is preparing.
Where HTMX comes from
HTMX is the direct successor to intercooler.js, a library born in the early 2010s that already added declarative AJAX to HTML — but depended on jQuery. Rewritten from scratch, with no dependencies, it became htmx. The project is led by Big Sky Software (Carson Gross and his team), proudly "made in Montana".
The result fits in a single script line: about 10 KB minified and compressed, zero dependencies. You add it to an existing page with no build, no bundler, no ecosystem to learn.
The why: what if HTML did more?
HTMX starts from four very simple questions about the limits of native HTML: why can only <a> and <form> issue a request? Why only on a click or a submit? Why only GET or POST? And why replace the whole page?
By lifting these four constraints, htmx "completes" HTML as hypertext: any element can trigger a request, on any event, with any HTTP method, and replace only a fragment of the page.
What HTMX brings
Concretely, htmx adds a few attributes: hx-get/hx-post (the request), hx-trigger (the event), hx-target (where to inject) and hx-swap (how to inject).
<button hx-post="/clicked" hx-swap="outerHTML">
Click me
</button>On click, the button sends a POST to /clicked and gets replaced by the response. Not a line of JavaScript. Above all: the server returns HTML, not JSON. No serialization, no display logic to duplicate between back and front.
Its place: hypermedia applications
HTMX isn't just a trick: it's a way of thinking, the hypermedia-driven application (HDA). The server stays the source of truth and returns HTML fragments; the client merely displays them. For purely local micro-interactivity (toggles, dropdowns), we sprinkle a bit of JS — Alpine.js pairs perfectly with it. The reference book, Hypermedia Systems, details this approach (free online).
HTMX doesn't replace everything. For a very client-stateful application (real-time editor, canvas, game), a JS framework still makes sense. But for a content site, a back-office, a dashboard or a CRUD — the vast majority of the web — the question is worth asking.
HTMX 2 vs HTMX 4: what changes
v4 is in the works (at four.htmx.org). It's not a surface lift: it modernizes the engine and hardens several default behaviors. Overview:
| Topic | HTMX 2 | HTMX 4 |
|---|---|---|
| HTTP transport | XMLHttpRequest | native fetch() |
| Attribute inheritance | implicit by default | explicit via :inherited |
| 4xx / 5xx responses | not swapped | swapped by default |
| Swap strategies | standard | + innerMorph / outerMorph, textContent, delete |
| Multi-target | <...> hx-swap-oob | + <hx-partial> |
| By HTTP status | global config | hx-status per status |
| View transitions | via extension | native (opt-in) |
| Default timeout | none | 60 s |
| Events | htmx:afterSwap… | htmx:after:swap (phase:action) |
| Extensions | hx-ext attribute | script included directly |
A few points deserve your attention. The move to fetch() is final (cannot be reverted). Attribute inheritance becomes explicit: you now have to add :inherited for an attribute to descend the DOM tree. And above all, 4xx/5xx responses are now swapped: if your server returns HTML with a 422 or a 500, that HTML is inserted into the target. It's up to you to design your error pages accordingly, or to use the new hx-status attribute.
<form hx-post="/save"
hx-status:422="swap:innerHTML target:#errors"
hx-status:5xx="swap:none">
<!-- fields -->
</form>v4 also brings morph swaps (idiomorph algorithm: innerMorph/outerMorph) that preserve DOM state, the <hx-partial> element to target several zones from a single response, native view transitions (opt-in), and a 60s default timeout. Events are renamed following a consistent htmx:phase:action scheme (e.g. htmx:afterSwap becomes htmx:after:swap).
Good news for migration: two lines of config — or the htmx-2-compat extension — restore v2 behavior, and a command-line tool (upgrade-check) scans your templates to list what needs to change.
<script>
htmx.config.implicitInheritance = true;
htmx.config.noSwap = [204, 304, '4xx', '5xx'];
</script>What to do with HTMX?
The most natural use cases: live search, pagination / infinite scroll, server-validated forms, refreshed dashboards without a reload, inline editing (CRUD), lazy loading of fragments and modals. Here's an instant search example:
<input type="search" name="q"
hx-get="/recherche"
hx-trigger="input changed delay:300ms"
hx-target="#resultats">
<div id="resultats"></div>With us, on Punky Tools (CodeIgniter 4), htmx is the backbone of the admin and the stats interfaces: we return Blade fragments, htmx inserts them, Alpine handles the micro-interactivity. Zero reloads, zero client state to maintain — and code we re-read without breaking a sweat.
The best JavaScript framework is sometimes the one you don't have to write.