htmx 4.0 Released: Breaking Changes & Migration Guide
htmx 4.0 launched Aug 28, 2026 with breaking changes: explicit attribute inheritance, unified events, and fetch(). See new features and htmx 2 vs Alpine.js.
htmx 4.0 was released on August 28, 2026, the next major version of htmx. The numbering jumped straight from 2.x to 4.0, skipping htmx 3 entirely. The two biggest changes are that attribute inheritance is now explicit instead of implicit, and the internal implementation moved from XMLHttpRequest to fetch(). There is no need to migrate existing projects immediately: npm's latest tag stays on the 2.x line until early 2027, and htmx 2 will continue to be supported indefinitely. Unless you want the new features in a fresh project, existing projects do not need to rush an upgrade.
What htmx is (a quick refresher)
htmx is a library that lets you update part of a page with server-returned HTML fragments — without a full page reload — simply by adding HTML attributes such as hx-get, hx-post, and hx-target. Unlike React or Vue, it has no client-side state management of its own; instead it drives Ajax, WebSocket, and SSE-style dynamic behavior declaratively through server-side rendering plus HTML attributes. This "extend HTML as-is" philosophy is often described as a HATEOAS-style, hypermedia-driven approach. The idea of driving the UI from the server is not unique to htmx — stacks such as Hono + Inertia + React keep routing and state on the server in a similar spirit.
htmx 4.0 changes at a glance
| Item | htmx 2 | htmx 4 |
|---|---|---|
| Internal transport | XMLHttpRequest | fetch() |
| Attribute inheritance | Implicit (auto-inherited by children) | Explicit (requires :inherited suffix) |
| Event naming | Mixed camelCase, e.g. htmx:beforeRequest | Unified htmx:phase:action[:sub-action] |
| XHR-specific events | Present (htmx:xhr:*) | Removed (no longer needed with fetch()) |
| Validation events | Present (htmx:validation:*) | Removed (replaced by native browser validation) |
| History cache | localStorage snapshots | Removed; refetches on back navigation (restorable via hx-history-cache extension) |
| Morph swap | Separate extension | Bundled by default |
| OOB swap syntax | hx-swap-oob attribute | New <hx-partial> tag added |
| Reactivity | None (server round-trip assumed) | New hx-live (HATEOAS-oriented) |
| npm distribution tag | latest | next (until early 2027) |
Breaking change 1: explicit attribute inheritance
In htmx 2, attributes such as hx-confirm or hx-target placed on a parent element were automatically inherited by child elements unless explicitly disabled with hx-disinherit. That implicit inheritance was convenient but also a common source of unintended behavior and hard-to-trace bugs. htmx 4 removes this implicit behavior entirely: any attribute you want a child to inherit must now carry an explicit :inherited suffix.
<!-- htmx 2: hx-confirm is implicitly inherited by the child button -->
<div hx-confirm="Are you sure?">
<button hx-delete="/item/1">Delete</button>
</div>
<!-- htmx 4: inheritance must be explicit via :inherited -->
<div hx-confirm:inherited="Are you sure?">
<button hx-delete="/item/1">Delete</button>
</div>
As a result, the hx-disinherit attribute used to opt out of inheritance is no longer needed and can be removed. Places in existing templates that relied on implicit inheritance can be flagged mechanically by the migration tool described below.
Breaking change 2: unified event naming
htmx 2's event names mixed camelCase and other conventions inconsistently. htmx 4 standardizes them into a single pattern: htmx:phase:action[:sub-action]. Alongside this, XHR-specific events (htmx:xhr:*), which became unnecessary once the library moved to fetch(), and validation events (htmx:validation:*), superseded by native browser form validation, have both been removed.
| htmx 2 | htmx 4 |
|---|---|
htmx:beforeRequest | htmx:before:request |
htmx:afterRequest | htmx:after:request |
htmx:beforeSwap | htmx:before:swap |
htmx:afterSwap | htmx:after:swap |
htmx:configRequest | htmx:config:request |
htmx:xhr:* (various) | Removed (unified under fetch()) |
htmx:validation:* (various) | Removed (native validation instead) |
Breaking change 3: the move to fetch() and history behavior
htmx 4 fully migrates its internal transport from XMLHttpRequest to fetch(). This change is said to be transparent for most users — visible behavior shouldn't change — but any custom code or extensions that hook into XHR-specific events will need to be rewritten. htmx 4 also drops the localStorage-based page snapshotting that htmx 2 used to speed up browser back-navigation restoration. The rationale is that third-party libraries' DOM mutations could leak into those snapshots and persist unexpectedly after restoration. In htmx 4, back navigation now refetches the <body> or the designated [hx-history-elt] element each time. If you want the old sessionStorage-style caching behavior back, you can install the hx-history-cache extension.
New features: morph swap, hx-partial, hx-live, and new extensions
- Morph swap bundled by default: an improved idiomorph algorithm is built in, so DOM-diff morphing works without installing a separate extension
- <hx-partial> tag: a custom element that expresses out-of-band (OOB) swaps and multi-element updates more clearly
- hx-live: a new HATEOAS-oriented reactivity mechanism that doesn't assume a server round-trip for every update
- New extensions: hx-preload (preloads content on mouseover), hx-download (native, fetch-based file downloads), hx-alpine-compat (smooths compatibility with Alpine.js), hx-sse (Server-Sent Events streaming), hx-ws (WebSocket streaming), hx-multipart (multipart/mixed streaming)
- htmax.js bundle: a single file that packages htmx with popular extensions
<!-- OOB swap example using hx-partial -->
<hx-partial hx-target="#messages" hx-swap="beforeend">
<div>New message</div>
</hx-partial>Installation and version strategy
Via CDN, 4.0 can be loaded with the following URL.
<script src="https://unpkg.com/htmx.org@4.0.0/dist/htmx.min.js"></script>The key thing to note is that npm's latest tag stays on the 2.x line until early 2027; the 4.0 line is distributed under the next tag instead. This is deliberate: it prevents users pinning to a version-less CDN URL (or the latest npm tag) from being force-upgraded into 4.0's breaking changes without warning. If you install via npm, you need to specify the version explicitly, e.g. 4.0.0. It's also worth noting that htmx 2 will continue to be supported indefinitely, so there's no need to feel pressure to migrate quickly.
Migration steps (the shortest path)
If you're considering migrating to htmx 4, start by running the official upgrade checker against your template directory.
npx htmx.org@4.0.0 upgrade-check -- ./templatesThis command scans files with extensions such as .html, .php, .js, .ts, .jinja/.jinja2/.j2, .erb, and .hbs, and flags places relying on implicit attribute inheritance, renamed attributes, removed attributes, references to old event names, and use of deprecated APIs. An AI agent skill set is also provided (htmx-guidance, htmx-debugging, htmx-extension-authoring, htmx-upgrade-from-htmx2) to assist automated migration with AI coding assistants. Based on what the checker finds, the basic workflow is: add :inherited where inheritance is actually needed, rewrite event listener names, and review any code depending on the now-removed XHR-specific or validation events.
How it compares to existing tools
| Item | htmx 2 | htmx 4 | Alpine.js | Hotwire Turbo | React |
|---|---|---|---|---|---|
| Core philosophy | HTML attribute extension + server round-trip | Same, transport moved to fetch() | Lightweight client-side reactivity | Rails-centric SPA-like behavior | Client-side virtual DOM & components |
| Transport | XMLHttpRequest | fetch() | No built-in transport | fetch built in | Arbitrary (fetch/axios, etc.) |
| State management | Mostly server-side | Mostly server-side, plus hx-live | Client-side (x-data) | Mostly server-side | Client-side (useState, etc.) |
| DOM diffing/morphing | Via a separate extension | Morph swap bundled by default | Not addressed (manual DOM ops) | Handled via Turbo Streams | Virtual DOM diffing |
| Learning curve | Low (attribute-based) | Low (attribute-based, slightly more inheritance rules) | Low (lightweight, standalone use) | Moderate (tied to Rails conventions) | High (JSX, state management, build tooling) |
| Typical use case | Server-rendered apps | Same, plus streaming UI | Light, decorative interactivity on existing pages | Turning Rails apps SPA-like | Large, client-driven SPAs |
Should you migrate? — decision criteria
For a new project, it can be worth explicitly pinning the next tag and adopting 4.0 from day one, to get the fetch()-based implementation, bundled morph swap, and the reactivity boost from hx-live. Keep in mind, though, that because it's still tagged next, further minor spec changes are possible before it stabilizes. For an existing project, the two breaking changes — explicit attribute inheritance and renamed events — touch a lot of surface area, so staying on the 2.x line (latest) is the reasonable default unless there's a specific reason to move. Since htmx 2's indefinite support has been explicitly stated, "not migrating" is a fully supported choice. If you do decide to migrate, the safe approach is to run upgrade-check first to map out the blast radius before planning the work. If you want to weigh it against a client-side routing approach, compare it with SPA-oriented options such as TanStack Router + Tailwind CSS + Vite.
Does htmx 3 exist?
No. htmx skipped version 3 entirely, moving directly from the 2.x line to 4.0.
Do I need to migrate from htmx 2 to 4.0 right away?
No. htmx 2 will continue to be supported indefinitely, and npm's latest tag stays on the 2.x line until early 2027. There's no need to rush.
How did attribute inheritance change?
In htmx 2, attributes like hx-confirm placed on a parent were implicitly inherited by child elements. In htmx 4, any attribute you want inherited must carry an explicit :inherited suffix.
Will my existing event listeners keep working?
No. Old event names like htmx:beforeRequest have been renamed to the new htmx:before:request style, and XHR-specific and validation events have been removed entirely.
Where should I start if I want to migrate?
Run npx htmx.org@4.0.0 upgrade-check -- ./templates first to detect implicit inheritance, old event names, and other affected areas before planning the migration.
Feel free to contact us
Contact Us