# HTMX 4 is here

> HTMX 4 moves to fetch, makes inheritance explicit, cleans up events and history, and adds morph swaps and multi-target partials.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-08-29 | Topics: [htmx](https://flaviocopes.com/tags/htmx/) | Canonical: https://flaviocopes.com/htmx-4/

[HTMX 4.0.0 was released](https://four.htmx.org/announcements/2026-08-28-htmx-4.0.0-is-released) on August 28, 2026.

This is a major release, but HTMX is still HTMX.

You add attributes to HTML. Those attributes make HTTP requests. The server returns HTML, and HTMX puts that HTML into the page.

If HTMX is new to you, start with my free [HTMX Course](https://flaviocopes.com/courses/htmx/) or my [deep dive into HTMX](https://flaviocopes.com/htmx-introduction/). I also wrote about [why I use HTMX](https://flaviocopes.com/why-i-use-htmx/) in the first place.

I included HTMX as the H in [the AHA Stack](https://ahastack.dev), together with Astro and Alpine.js. Astro renders the HTML, HTMX handles server requests and HTML updates, and Alpine.js manages small pieces of local state.

HTMX 4 keeps that model. Let's see the major changes.

## HTMX now uses fetch

HTMX used `XMLHttpRequest` because it once supported older browsers.

HTMX 4 replaces it with the browser's native `fetch()` API.

For most applications, this is an internal change. Your `hx-get`, `hx-post`, targets, and swaps still work in the same way.

The new foundation makes streaming easier and gives extensions a cleaner way to work with requests.

## Attribute inheritance is now explicit

This is the biggest change when upgrading an existing application.

In HTMX 2, some attributes on a parent automatically applied to its children:

```html
<div hx-confirm="Are you sure?">
  <button hx-delete="/items/1">Delete</button>
</div>
```

In HTMX 4, add `:inherited` when you want that behavior:

```html
<div hx-confirm:inherited="Are you sure?">
  <button hx-delete="/items/1">Delete</button>
</div>
```

An attribute placed directly on an element works as before.

I like this change. You can look at the parent and see that its behavior reaches the children. There is less invisible behavior to remember.

## Event names are more consistent

HTMX 2 event names grew over time. They did not follow one clear pattern.

HTMX 4 events use `htmx:phase:action`:

| 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` |

Most request errors now use `htmx:error`. HTTP error responses use `htmx:response:error`.

If you listen to HTMX events in JavaScript or with `hx-on`, you must check those names during the upgrade.

## History no longer stores page snapshots by default

HTMX 2 stored DOM snapshots in `localStorage` for back-button navigation.

That could save a page after another JavaScript library changed it. Restoring that snapshot did not always restore the JavaScript state behind it.

HTMX 4 fetches the page again when you go back. It then swaps the response into `<body>`, or into the element marked with `hx-history-elt`.

This removes a lot of hidden state. If you want local history caching, HTMX now provides the `hx-history-cache` extension.

## Error responses are swapped by default

There is another upgrade detail worth knowing.

HTMX 2 did not swap `4xx` and `5xx` responses by default. HTMX 4 swaps every response except `204` and `304`.

This is useful when your server returns validation HTML with a `422` status. But an existing application might return an error page that was never meant to appear inside a small target.

Check your error responses before upgrading. You can use `hx-status` when each status needs a different target or swap behavior.

## Morph swaps are built in

A normal swap replaces part of the DOM.

HTMX 4 also includes `innerMorph` and `outerMorph`:

```html
<button
  hx-get="/profile"
  hx-target="#profile"
  hx-swap="outerMorph">
  Refresh profile
</button>
```

A morph changes the existing DOM to match the new HTML. It can preserve things like focus, input state, and a playing video because it does not replace every node.

Morphing uses more CPU than a normal swap. I would use it where preserving DOM state matters, not as the default everywhere.

## One response can update several targets

HTMX already supports out-of-band swaps. HTMX 4 adds a clearer option called `<hx-partial>`.

The server can return several updates, and each one names its own target:

```html
<hx-partial hx-target="#messages" hx-swap="beforeend">
  <div>New message</div>
</hx-partial>

<hx-partial hx-target="#message-count">
  <span>5</span>
</hx-partial>
```

One response can append a message and update the counter. The intent is visible in the response itself.

## Extensions got more interesting

HTMX 4 has a new event-based extension system.

There are extensions for preloading, file downloads, optimistic updates, Server-Sent Events, WebSockets, streaming multipart responses, and history caching.

The `hx-alpine-compat` extension is especially interesting for AHA Stack applications. It initializes Alpine.js on new fragments before HTMX swaps them into the page.

There is also an optional `htmax.js` bundle. It packages HTMX with its most popular extensions in one file.

## How I would upgrade an AHA Stack application

I would not change everything at once.

First, I would run the official upgrade checker:

```bash
npx htmx.org@4 upgrade-check -- ./src
```

It finds inherited attributes, old event names, removed attributes, and old API calls. The tool requires Python 3.

Then I would make inheritance explicit and rename my event listeners. I would also inspect every `4xx` and `5xx` response that returns HTML.

HTMX includes an `htmx-2-compat` extension if an application needs a gradual migration. It restores old event names, implicit inheritance, and the previous error-swapping defaults.

After the application works, I would test morph swaps in parts of the interface where focus or local state matters. In an AHA Stack application, I would also test `hx-alpine-compat` anywhere an HTMX response adds Alpine.js components.

You can read the complete details in the official [HTMX 4 migration guide](https://four.htmx.org/migration-guide-htmx-4/).

## You do not have to upgrade today

HTMX 2 will continue to be supported.

It also remains the `latest` npm release until early 2027. This avoids silently upgrading sites that use a CDN URL without a version.

To install version 4 now, ask for the major version explicitly:

```bash
npm install htmx.org@4
```

HTMX 4 feels like a careful cleanup, not a new framework.

That is exactly what I want from a library like HTMX.
