Back to Blog

htmx 4.0: What Breaks, and How the Release Avoids Breaking You

htmx 4.0 makes inheritance explicit and swaps error responses, but the real lesson is the rollout: latest stays on 2.x, a checker ships, and key behaviors get revert flags.

Umut Korkmaz

Published: 4 min read

htmx 4.0.0 was released on August 28, after what the team describes as eight months of work. The headline changes are real: attribute inheritance is now opt-in, event names follow a new scheme, history no longer caches pages in localStorage, and the internals moved from XMLHttpRequest to fetch(). What caught my attention, though, is how carefully the release is staged.

What changed

The announcement says that from a user's point of view htmx 4 is almost identical to htmx 2, with three major changes:

  • Inheritance is explicit. In htmx 2, an attribute like hx-confirm on a parent applied to its children. In htmx 4 you have to say so with a suffix: hx-confirm:inherited="Are you sure?". hx-disinherit is gone because it is no longer needed. The team calls this the largest upgrade burden.
  • Events follow htmx:phase:action. htmx:beforeRequest becomes htmx:before:request, htmx:afterSwap becomes htmx:after:swap, most error events collapse into htmx:error, and HTTP errors fire htmx:response:error. The htmx:xhr:* and htmx:validation:* events are removed.
  • History re-fetches. On back navigation htmx fetches the page again and swaps it into <body>. The old localStorage snapshot could hold DOM changes made by third-party scripts whose logic was gone after a restore. An hx-history-cache extension restores from sessionStorage if you still want caching.

On the new side there are built-in morph swaps (innerMorph and outerMorph, based on idiomorph) and an <hx-partial> tag that targets several elements from one response, as a clearer alternative to out-of-band swaps.

The changes that fail quietly

The What's New in htmx 4 guide lists more breaking changes than the announcement. In these, nothing obviously breaks; the app just behaves differently:

  • htmx 4 swaps all HTTP responses except 204 and 304. If your server returns an HTML error page with a 422 or 500, it now lands inside the target. htmx 2 did not swap 4xx and 5xx responses.
  • The default timeout is now 60 seconds. htmx 2 had none.
  • hx-delete no longer includes the enclosing form's inputs. The guide's fix is hx-include="closest form".
  • Out-of-band elements now swap after the main content, not before it.
  • hx-disable becomes hx-ignore, and hx-disable now does what hx-disabled-elt used to do. The guide says to do the hx-disable rename before upgrading, and before renaming hx-disabled-elt.

The sample upgrade checker output on the announcement page shows the nastiest case. An hx-headers attribute on a parent that carries a CSRF token no longer reaches child elements without :inherited, so the server rejects the request. In your logs that will probably look like a CSRF or auth problem, not an htmx problem.

The release mechanics are worth copying

  • latest stays on 2.x. The 4.0 line is tagged next on npm until some point in early 2027, because the team does not want to force-upgrade people who load htmx from unversioned CDN URLs.
  • A checker ships with the release. npx [email protected] upgrade-check -- ./templates reports missing :inherited suffixes, renamed and removed attributes, old event names and removed API calls, with file and line numbers.
  • The riskiest behavior changes come with revert switches. htmx.config.implicitInheritance = true, htmx.config.noSwap = [204, 304, '4xx', '5xx'] and htmx.config.defaultTimeout = 0, plus an htmx-2-compat extension that restores htmx 2 defaults and event names. The fetch() move is explicitly marked as not revertible. The guide lists no flag for the out-of-band ordering change.
  • htmx 2 stays supported indefinitely, so there is no deadline pressure.
  • There is an upgrade skill for LLMs, htmx-upgrade-from-htmx2, alongside guidance and debugging skills.

Put together, a team can move one behavior at a time, with a tool that knows the exact list of breaking changes. When I think about a future breaking release of my own packages, like Meridian on NuGet, the checker and the per-behavior flags are the two ideas I would copy first. A changelog tells people what changed. A checker tells them where it touches their code.

What I would check on Monday

If a team runs htmx 2, this is my list:

  1. Find unversioned htmx script URLs and pin them to an exact version. latest is safe for now, but only until early 2027.
  2. Run the checker on a branch. The default scan covers .html, .php, .js, .ts, Jinja, ERB and Handlebars files. Razor views or other template types need --ext, for example --ext .cshtml.
  3. List every endpoint that returns HTML on a 4xx or 5xx and decide what should happen: design it as swap content, route it per status with hx-status:422="swap:innerHTML target:#errors", or keep the old behavior with noSwap.
  4. Grep your JavaScript for old event names. The checker looks in hx-on attributes and in scripts where it can, but custom event wiring deserves a manual pass.
  5. Look for slow requests, such as exports and reports, that can run longer than the new 60-second default timeout.

My take

htmx 4 is a reasonable major version, but its release engineering is the better lesson. Keeping latest on the old line, shipping a checker and giving the riskiest behavior changes their own revert switches turns one risky migration into several small ones. And since htmx 2 is supported indefinitely, the right first step for most teams is not the upgrade. It is pinning the version and running the checker, then planning the upgrade.

Sources

Primary references used for this post.

  1. four.htmx.org/announcements/2026-08-28-htmx-4.0.0-is-released (opens in a new tab)
  2. four.htmx.org/docs/whats-new-in-htmx-4 (opens in a new tab)

About the author

Umut Korkmaz

Lead Software Engineer

I'm a senior software engineer in Istanbul. Here I write about the tools and releases I've shipped to production myself, and what they taught me along the way.

Picked by shared topics.