htmx: Build Interactive Web Apps with HTML · Beyond the Basics
Upgrading to htmx 4
Plan an upgrade from htmx 2 to htmx 4: explicit inheritance, new event names, error swapping, renamed attributes and the upgrade checker.
What you'll build
An upgrade plan for the contacts app, and a version of the delete-row markup that behaves the same in htmx 2 and htmx 4.
The idea
htmx 4.0 shipped on 28 August 2026. htmx 2 stays supported indefinitely, and npm keeps 2.x as latest until
some point in early 2027, so you can upgrade when it suits you. The changes that affect most apps:
| Area | htmx 2 | htmx 4 |
|---|---|---|
| Transport | XMLHttpRequest | fetch() (streaming responses) |
| Inheritance | implicit | explicit: hx-target:inherited="..." |
| 4xx/5xx responses | not swapped | swapped (except 204, 304); hx-status:422="..." for control |
| Events | htmx:afterRequest |
htmx:after:request |
| Attributes | hx-disable, hx-disabled-elt |
hx-ignore, hx-disable |
| History | snapshots in sessionStorage |
re-fetches the page on Back |
| Timeout | none | 60 seconds |
| Extensions | hx-ext="sse" |
include the script; no hx-ext |
Steps: read the official guide, run npx [email protected] upgrade-check -- ./templates, rename hx-disable →
hx-ignore then hx-disabled-elt → hx-disable, add :inherited (or set htmx.config.implicitInheritance = true temporarily), rename event listeners, and review error handling.
The code
Writing attributes on each element instead of relying on inheritance gives markup that works the same in both versions:
<table><tbody>
<tr><td>Ada</td><td><button hx-delete="/contacts/1" hx-confirm="Delete Ada?"
hx-target="closest tr" hx-swap="outerHTML">Delete</button></td></tr>
<tr><td>Grace</td><td><button hx-delete="/contacts/2" hx-confirm="Delete Grace?"
hx-target="closest tr" hx-swap="outerHTML">Delete</button></td></tr>
</tbody></table>
<!-- The pretend server: on a real site this part is your backend (Python, PHP, Node, Go...) -->
<script>
server.delete("/contacts/:id", () => "");
</script>
In htmx 4 the inherited version needs the suffix:
<tbody hx-confirm:inherited="Delete this contact?" hx-target:inherited="closest tr" hx-swap:inherited="outerHTML">
Common mistakes
- Renaming
hx-disabled-elttohx-disablebefore renaming the oldhx-disabletohx-ignore: the two meanings get mixed up. - Forgetting that error responses now swap: an error page can land in a small target. Add
hx-statusrules or return small error fragments. - Upgrading before your extensions (SSE, WebSockets, others) support htmx 4.
- Leaving
implicitInheritanceon permanently instead of finishing the migration.
Key points
- htmx 2 remains supported; upgrade deliberately.
- Inheritance, event names and error swapping are the main changes.
- Use the upgrade checker and rename attributes in the right order.
Further reading: What's new in htmx 4 · htmx 4.0.0 release · Tutorial: Upgrading to htmx 4
Practice questions
What is the largest change when moving from htmx 2 to htmx 4?
Answer
Attribute inheritance becomes explicit: attributes that should apply to descendants need the :inherited suffix.
In which order should you rename hx-disable and hx-disabled-elt when upgrading?
Answer
First hx-disable → hx-ignore, then hx-disabled-elt → hx-disable; the other order mixes the two meanings.
How does error-response handling differ between htmx 2 and 4?
Answer
htmx 2 doesn't swap 4xx/5xx; htmx 4 swaps everything except 204 and 304, with hx-status for per-code control.
Open in the app to mark lessons complete, take notes and track your progress.