htmx 4.0 ships, moving to fetch() from XMLHttpRequest

The htmx team announced the release of htmx 4.0.0, the result of about 8 months of work. The project traces back to when the team's lead started building a smaller library called fixi and, in the process, got familiar with the fetch() API and async JavaScript; htmx itself had used XMLHttpRequest all along for backwards compatibility. A conversation with Christian Tanul about streaming HTML suggested that moving htmx's internals to fetch() would simplify things generally, and after that the team brought on Michael West and Alex Petros to build it out. Development proceeded as a port of fixi plus the existing htmx test suite, and the team says that as they worked they kept rediscovering why htmx 2.x behaved the way it did, so the final 4.x behavior ended up close to 2.x, with a small number of deliberate divergences the team frames as choices meant to support what it calls '100-year web services'.

Crucially, htmx 4.0 is not being tagged 'latest' on NPM. The team says it does not want to force-upgrade users who rely on non-versioned CDN URLs for htmx, so 2.x stays 'latest' and the 4.0 line stays on the 'next' tag until some point in early 2027; the project's website will reference 4.0 in the meantime. From a user's perspective the team describes three major changes. First, attribute inheritance, which let a parent element's htmx attributes apply automatically to its children, is now explicit rather than implicit: an attribute like hx-confirm only inherits down the DOM if it is written as hx-confirm:inherited. The team calls this the single biggest upgrade item and is shipping a command line tool to find places in a codebase that need the suffix added; attributes like hx-disinherit, used in 2.x to opt out of inheritance, are no longer needed and should be removed. Second, htmx's event names have been standardized to a htmx:phase:action[:sub-action] pattern; most error events now collapse into a single htmx:error, HTTP error responses fire htmx:response:error, the htmx:xhr:* events are gone because htmx now uses fetch(), and the htmx:validation:* events are removed in favor of native browser form validation. Third, history support no longer caches pages in localStorage by default. In 2.x, that cache could capture DOM mutations made by third-party JavaScript, and restoring the cached snapshot on back navigation would bring those mutations back without the JavaScript logic that produced them. In 4.0, back navigation instead re-fetches the page and swaps it into (or into an [hx-history-elt] element if one exists), which the team says lets third-party scripts 'just work' in most cases; anyone who wants local caching back can add the new hx-history-cache extension, which restores history from sessionStorage and is built to play well with libraries like Alpine.js.

On top of these changes, htmx 4 adds two new features the team highlights: built-in morph swaps, based on an algorithm called idiomorph that Michael West refined and integrated directly into the library, and a new tag for updating parts of a page in ways that a simple element swap or an out-of-band swap does not cleanly express, such as appending a new message to a #messages container or updating a #count element elsewhere on the page in the same response. The switch to fetch() internally also opened up the extension model, and the release ships several new or reworked extensions: hx-preload (preloads content, for example on mouseover, to speed up later requests), hx-download (native, fetch-based file downloads), hx-alpine-compat (smooths over compatibility issues with Alpine.js) and the history-caching hx-history-cache extension described above, plus three streaming extensions: hx-sse for Server-Sent Events over text/event-stream, hx-ws for WebSockets, and hx-multipart for streaming over multipart/mixed. The team also introduced its own front-end scripting layer, hx-live, described as inspired by Alpine.js, jQuery and hyperscript and built to support what the announcement calls 'DOM-based, HATEOAS-friendly reactivity'. Developers who do not want to choose extensions individually can instead use a new htmax.js bundle that packages htmx with the most popular ones in a single file.

For migration, the team is shipping a CLI upgrade checker, run as 'npx htmx.org@4.0.0 upgrade-check', that scans template and script files (HTML, PHP, JS, TS, Jinja, ERB, Handlebars by default, with more extensions addable) for exactly the kinds of breakage the new version introduces: attributes that need the new :inherited suffix, attributes that were renamed (hx-disable becomes hx-ignore, since hx-disable now means something different, 'disable during request') or removed outright (hx-vars is gone in favor of hx-vals with a js: prefix, and hx-prompt now requires loading a separate extension to keep the old syntax), old event names used in hx-on attributes or JavaScript, and removed APIs such as htmx.addClass(), which callers should replace with the native element.classList.add(). A sample run against a single template file found 8 issues. The team is also shipping an 'agent skill' meant to assist with the upgrade, separate from a set of four skills files for large language models covering general htmx guidance, debugging, writing extensions, and migrating a codebase from htmx 2.x to 4.x; the announcement notes, without resolving it, that 'whether releasing a new version of a library in the LLM era is a good or bad thing' is a separate question. htmx 4.0.0 can be installed through a package manager pinned to that version, loaded from a CDN via a script tag pointing at unpkg.com's htmx.org@4.0.0 build, or downloaded directly. The team closes by stressing that htmx 2 will keep being supported indefinitely, so there is no pressure to move to 4.0, and thanks seven named contributors: Michael West, Christian Tanul, Alex Petros, Stephen Mitchell (credited for an unspecified game built during the project), Stu Kennedy (the team's WebSockets expert), Andre Ahlert Jr. (IDE and editor support) and Dien Hoa Truong (early testing and bug fixes). The post was submitted to Hacker News by user rmsaksida and had drawn 601 points and 146 comments within about 17 hours.

Key facts

  • htmx 4.0.0 ships after about 8 months of development, with the library's internals moved from XMLHttpRequest to fetch().
  • Attribute inheritance switches from implicit to explicit by default; an attribute now inherits to child elements only when written with an :inherited suffix, such as hx-confirm:inherited, and a CLI tool flags where the change is needed.
  • Event names are standardized to a htmx:phase:action[:sub-action] pattern; the htmx:xhr:* events are removed since htmx now uses fetch(), and htmx:validation:* events are replaced by native browser form validation.
  • History navigation no longer caches pages in localStorage by default; back navigation instead re-fetches and swaps the page, with an optional hx-history-cache extension for those who want sessionStorage-based caching back.
  • htmx 2.x stays the npm 'latest' tag and remains supported indefinitely, while 4.0 sits on the 'next' tag until early 2027 so CDN users on unversioned URLs are not force-upgraded.

Why it matters

htmx is a small library that lets developers add dynamic, server-driven behavior to HTML through attributes rather than writing custom JavaScript, and 4.0 is its first release built around fetch() instead of XMLHttpRequest, the API it had used since its earliest days. The team frames the rewrite as more than a technical swap: it says the internal change opened up new possibilities, particularly for the three streaming extensions (Server-Sent Events, WebSockets and multipart streaming) and for a more flexible extension architecture generally. At the same time the team is explicit that it kept behavioral differences between 2.x and 4.x deliberately small, saying the goal was to put htmx-based applications in a good position to become what it calls '100-year web services', meaning a library that changes its plumbing without breaking the contract long-lived sites depend on.

Who it affects

Anyone building or maintaining a project on htmx, whether through a bundler-installed package or a CDN script tag. Because npm's 'latest' tag still points at 2.x and will keep doing so until early 2027, most existing installs are unaffected unless a developer deliberately opts into version 4.0.0. Developers who write custom JavaScript against htmx's events or APIs are affected more directly, since old event names and some APIs (like htmx.addClass()) are removed. Authors of htmx extensions are also affected, since the fetch()-based internals reshape how extensions are written; the release itself ships several new ones. The shipped skills files for large language models suggest the team is also targeting developers who lean on AI coding assistants to write or migrate htmx code.

How to use it

htmx 4.0.0 can be added through a package manager pinned to that exact version, loaded from a CDN with a script tag pointing at unpkg.com's htmx.org@4.0.0 build, or downloaded directly; it will not arrive automatically for anyone tracking npm's 'latest' tag, which stays on 2.x. Before migrating, the team recommends running its CLI checker with 'npx htmx.org@4.0.0 upgrade-check' against a project's template and script files; it reports where attributes need the new :inherited suffix, where attributes were renamed or removed, and where old event names or removed APIs appear in code. New capabilities, morph swaps and the tag, work out of the box, while extras like hx-preload, hx-download, hx-alpine-compat, hx-history-cache, hx-sse, hx-ws and hx-multipart are opt-in extensions, or can be pulled in together via the bundled htmax.js file. The library and its extensions are distributed as open source; the source text gives no pricing or licensing terms beyond that.

How solid is it

The account comes directly from the htmx project's own announcement page, written in the first person by what appears to be the project's lead alongside credits to a small, named team, so the technical claims about what changed are the team describing its own work rather than an independent review. The post drew significant attention on Hacker News, 601 points and 146 comments within about 17 hours of submission by user rmsaksida, which points to strong developer interest in the release, though that engagement speaks to interest rather than to whether the migration claims hold up in practice. Specific, checkable details are included, such as the CLI tool's sample scan finding 8 issues in a single test file, which supports that the tooling exists and runs as described.

Risks and caveats

The team names its own biggest risk: the shift from implicit to explicit attribute inheritance is, in its words, the largest upgrade burden, and any htmx 2.x app that relies on inherited attributes will misbehave under 4.0 until those attributes are marked with :inherited, something easy to miss without running the upgrade checker. Custom code that listens for old htmx event names, or calls removed APIs like htmx.addClass(), will silently stop working after an upgrade rather than failing loudly. The team's assurance that 'most people won't notice' the loss of localStorage-based history caching is its own assessment, not something independently verified against real deployments, and the announcement gives no performance numbers to back its claim that the fetch() migration is 'transparent for most users'. Finally, no firm date is given for when 2.x will actually stop being npm's 'latest' tag, only that 4.0 is expected to leave the 'next' tag 'some point in early 2027'.

“we do not want to force-upgrade users who are relying on non-versioned CDN URLs for htmx”

— the htmx team, in the release announcement