# Migration
## Quick Start
There are two major behavioral changes between htmx 2.x and 4.x:
* In htmx 2.0 attribute inheritance is *implicit* by default while in 4.0 it is explicity by default
* In htmx 2.0, `400` and `500` response codes are not swapped by default, whereas in htmx 4.0 these requests will be
swapped
Add these two config lines to restore htmx 2.x behavior:
```html
<script>
htmx.config.implicitInheritance = true;
htmx.config.noSwap = [204, 304, '4xx', '5xx'];
</script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@next/dist/htmx.min.js"></script>
```
[`implicitInheritance`](https://four.htmx.org/reference/config/htmx-config-implicitInheritance) restores htmx 2's implicit attribute
inheritance. [`noSwap`](https://four.htmx.org/reference/config/htmx-config-noSwap) prevents swapping error responses.
Or load the [`htmx-2-compat`](https://four.htmx.org/docs/extensions/htmx-2-compat) extension, which restores implicit inheritance, old event
names, and previous error-swapping defaults:
```html
<script src="/path/to/htmx.js"></script>
<script src="/path/to/ext/htmx-2-compat.js"></script>
```
Most htmx 2 apps should work with either approach. Then migrate incrementally using this guide.
## What Changed
### `fetch()` replaces `XMLHttpRequest`
All requests use the native [`fetch()` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). This cannot be
reverted.
### Explicit inheritance
Add [`:inherited`](https://four.htmx.org/docs/features/attribute-inheritance) to any attribute that should inherit down the DOM tree.
```html
<!-- htmx 2: implicit inheritance -->
<div hx-confirm="Are you sure?">
<button hx-delete="/item/1">Delete</button>
</div>
<!-- htmx 4: explicit inheritance -->
<div hx-confirm:inherited="Are you sure?">
<button hx-delete="/item/1">Delete</button>
</div>
```
Works on any attribute: [`hx-boost`](https://four.htmx.org/reference/attributes/hx-boost)`:inherited`, [
`hx-target`](https://four.htmx.org/reference/attributes/hx-target)`:inherited`, [`hx-confirm`](https://four.htmx.org/reference/attributes/hx-confirm)`:inherited`,
etc.
Use `:append` to add to an inherited value instead of replacing it:
```html
<div hx-include:inherited="#global-fields">
<!-- appends .extra to the inherited value -->
<form hx-include:inherited:append=".extra">...</form>
</div>
```
Revert: [`htmx.config.implicitInheritance`](https://four.htmx.org/reference/config/htmx-config-implicitInheritance) `= true`
### Error responses swap
htmx 4 swaps all HTTP responses. Only [`204`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/204)
and [`304`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/304) do not swap.
htmx 2 did not swap `4xx` and `5xx` responses. In htmx 4, if your server returns HTML with a `422` or `500`, that HTML
gets swapped into the target. Design your error responses to work as swap content, or use [
`hx-status`](https://four.htmx.org/reference/attributes/hx-status) to control per-code behavior.
Revert: [`htmx.config.noSwap`](https://four.htmx.org/reference/config/htmx-config-noSwap) `= [204, 304, '4xx', '5xx']`
### [`hx-delete`](https://four.htmx.org/reference/attributes/hx-delete) excludes form data
Like [`hx-get`](https://four.htmx.org/reference/attributes/hx-get), [`hx-delete`](https://four.htmx.org/reference/attributes/hx-delete) no longer includes the
enclosing form's inputs.
Fix: add [`hx-include`](https://four.htmx.org/reference/attributes/hx-include)`="closest form"` where needed.
### No history cache
History no longer caches pages in [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).
When navigating back, htmx re-fetches the page and swaps it into `<body>`.
Use [`htmx.config.history`](https://four.htmx.org/reference/config/htmx-config-history) `= "reload"` for a full page reload instead. Use
`htmx.config.history = false` to disable.
### OOB swap order
In htmx 2, out-of-band ([`hx-swap-oob`](https://four.htmx.org/reference/attributes/hx-swap-oob)) elements swapped **before** the main
content.
In htmx 4, the main content swaps first. OOB and `hx-partial` elements swap after (in document order).
This matters if an OOB swap creates or modifies DOM that the main swap depends on. If your app relies on that ordering,
restructure so each swap is independent.
### 60-second timeout
htmx 2 had no timeout (`0`). htmx 4 sets [`defaultTimeout`](https://four.htmx.org/reference/config/htmx-config-defaultTimeout) to `60000`.
Revert: `htmx.config.defaultTimeout = 0`
### Extension loading
Include extension scripts directly. No attribute needed:
```html
<script src="/path/to/htmx.js"></script>
<script src="/path/to/ext/sse.js"></script>
```
Restrict which extensions can load:
```html
<meta name="htmx-config" content='{"extensions": "sse, ws"}'>
```
Extension authors use `htmx.registerExtension(name, methodMap)` to register.
See [Extensions documentation](https://four.htmx.org/docs/extensions/using-extensions) for details.
## Renames and Removals
### Rename `hx-disable`
Do this **before** upgrading. The name `hx-disable` has been reassigned:
- In htmx 2, `hx-disable` meant "skip htmx processing on this element"
- In htmx 4, that role is [`hx-ignore`](https://four.htmx.org/reference/attributes/hx-ignore)
- The name `hx-disable` now does what `hx-disabled-elt` used to do (disable form elements during requests)
Rename in this order to avoid conflicts:
1. Rename `hx-disable` to [`hx-ignore`](https://four.htmx.org/reference/attributes/hx-ignore)
2. Rename `hx-disabled-elt` to [`hx-disable`](https://four.htmx.org/reference/attributes/hx-disable)
### Removed attributes
| Removed | Use instead |
|------------------|---------------------------------------------------------------------------------------------------|
| `hx-vars` | [`hx-vals`](https://four.htmx.org/reference/attributes/hx-vals) with `js:` prefix |
| `hx-params` | [`htmx:config:request`](https://four.htmx.org/reference/events/htmx-config-request) event |
| `hx-prompt` | [`hx-confirm`](https://four.htmx.org/reference/attributes/hx-confirm) with `js:` prefix |
| `hx-ext` | [Include extension script directly](https://four.htmx.org/docs/extensions/using-extensions) |
| `hx-disinherit` | Not needed (inheritance is explicit) |
| `hx-inherit` | Not needed (inheritance is explicit) |
| `hx-request` | [`hx-config`](https://four.htmx.org/reference/attributes/hx-config) |
| `hx-history` | Removed (no [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)) |
| `hx-history-elt` | Removed |
### Renamed events
All events follow a new pattern: `htmx:phase:action[:sub-action]`
All error events are consolidated to [`htmx:error`](https://four.htmx.org/reference/events/htmx-error).
| htmx 2.x | htmx 4.x |
|-----------------------------|-----------------------------------------------------------------------------------|
| `htmx:afterOnLoad` | [`htmx:after:init`](https://four.htmx.org/reference/events/htmx-after-init) |
| `htmx:afterProcessNode` | [`htmx:after:init`](https://four.htmx.org/reference/events/htmx-after-init) |
| `htmx:afterRequest` | [`htmx:after:request`](https://four.htmx.org/reference/events/htmx-after-request) |
| `htmx:afterSettle` | [`htmx:after:swap`](https://four.htmx.org/reference/events/htmx-after-swap) |
| `htmx:afterSwap` | [`htmx:after:swap`](https://four.htmx.org/reference/events/htmx-after-swap) |
| `htmx:beforeCleanupElement` | [`htmx:before:cleanup`](https://four.htmx.org/reference/events/htmx-before-cleanup) |
| `htmx:beforeHistorySave` | [`htmx:before:history:update`](https://four.htmx.org/reference/events/htmx-before-history-update) |
| `htmx:beforeOnLoad` | [`htmx:before:init`](https://four.htmx.org/reference/events/htmx-before-init) |
| `htmx:beforeProcessNode` | [`htmx:before:process`](https://four.htmx.org/reference/events/htmx-before-process) |
| `htmx:beforeRequest` | [`htmx:before:request`](https://four.htmx.org/reference/events/htmx-before-request) |
| `htmx:beforeSwap` | [`htmx:before:swap`](https://four.htmx.org/reference/events/htmx-before-swap) |
| `htmx:configRequest` | [`htmx:config:request`](https://four.htmx.org/reference/events/htmx-config-request) |
| `htmx:historyCacheMiss` | [`htmx:before:history:restore`](https://four.htmx.org/reference/events/htmx-before-restore-history) |
| `htmx:historyRestore` | [`htmx:before:history:restore`](https://four.htmx.org/reference/events/htmx-before-restore-history) |
| `htmx:load` | [`htmx:after:init`](https://four.htmx.org/reference/events/htmx-after-init) |
| `htmx:oobAfterSwap` | [`htmx:after:swap`](https://four.htmx.org/reference/events/htmx-after-swap) |
| `htmx:oobBeforeSwap` | [`htmx:before:swap`](https://four.htmx.org/reference/events/htmx-before-swap) |
| `htmx:pushedIntoHistory` | [`htmx:after:history:push`](https://four.htmx.org/reference/events/htmx-after-push-into-history) |
| `htmx:replacedInHistory` | [`htmx:after:history:replace`](https://four.htmx.org/reference/events/htmx-after-replace-into-history) |
| `htmx:responseError` | [`htmx:error`](https://four.htmx.org/reference/events/htmx-error) |
| `htmx:sendError` | [`htmx:error`](https://four.htmx.org/reference/events/htmx-error) |
| `htmx:swapError` | [`htmx:error`](https://four.htmx.org/reference/events/htmx-error) |
| `htmx:targetError` | [`htmx:error`](https://four.htmx.org/reference/events/htmx-error) |
| `htmx:timeout` | [`htmx:error`](https://four.htmx.org/reference/events/htmx-error) |
### `hx-on::` shorthand
The double-colon shorthand for htmx events no longer works. Because event names changed from camelCase to
colon-separated (e.g. `htmx:afterRequest` → `htmx:after:request`), the `hx-on::` prefix can no longer map to the correct
event name.
Use the full event name instead:
```html
<!-- htmx 2 -->
<form hx-on::after-request="this.reset()">
<!-- htmx 4 -->
<form hx-on:htmx:after:request="this.reset()">
```
This applies to all `hx-on::` event handlers. Find and replace `hx-on::` with `hx-on:htmx:` and update the event name to
the new colon-separated format (see table above).
### Removed events
Validation events are removed. Use native browser form validation:
- `htmx:validation:validate`
- `htmx:validation:failed`
- `htmx:validation:halted`
XHR events are removed (htmx uses `fetch()` now):
| Removed | Use instead |
|----------------------|------------------------------------------------------------------|
| `htmx:xhr:loadstart` | No replacement |
| `htmx:xhr:loadend` | [`htmx:finally:request`](https://four.htmx.org/reference/events/htmx-finally-request) |
| `htmx:xhr:progress` | No replacement |
| `htmx:xhr:abort` | [`htmx:error`](https://four.htmx.org/reference/events/htmx-error) |
### Config changes
**Renamed:**
| htmx 2.x | htmx 4.x |
|--------------------------|----------------------------------------------------------------------------|
| `defaultSwapStyle` | [`defaultSwap`](https://four.htmx.org/reference/config/htmx-config-defaultSwap) |
| `globalViewTransitions` | [`transitions`](https://four.htmx.org/reference/config/htmx-config-transitions) |
| `historyEnabled` | [`history`](https://four.htmx.org/reference/config/htmx-config-history) |
| `includeIndicatorStyles` | [`includeIndicatorCSS`](https://four.htmx.org/reference/config/htmx-config-includeIndicatorCSS) |
| `timeout` | [`defaultTimeout`](https://four.htmx.org/reference/config/htmx-config-defaultTimeout) |
**Changed defaults:**
| Config | htmx 2 | htmx 4 |
|--------------------------------------------------------------------------|------------------|----------------------|
| [`defaultTimeout`](https://four.htmx.org/reference/config/htmx-config-defaultTimeout) | `0` (no timeout) | `60000` (60 seconds) |
| [`defaultSettleDelay`](https://four.htmx.org/reference/config/htmx-config-defaultSettleDelay) | `20` | `1` |
**Removed:**
`addedClass`, `allowEval`, `allowNestedOobSwaps`, `allowScriptTags`, `attributesToSettle`, `defaultSwapDelay`,
`disableSelector` (use [`hx-ignore`](https://four.htmx.org/reference/attributes/hx-ignore)), `getCacheBusterParam`, `historyCacheSize`,
`ignoreTitle` (still works per-swap via [`hx-swap`](https://four.htmx.org/reference/attributes/hx-swap)`="... ignoreTitle:true"`),
`methodsThatUseUrlParams`, `refreshOnHistoryMiss`, `responseHandling` (use [
`hx-status`](https://four.htmx.org/reference/attributes/hx-status) and [`noSwap`](https://four.htmx.org/reference/config/htmx-config-noSwap)), `scrollBehavior`,
`scrollIntoViewOnBoost`, `selfRequestsOnly` (use [`htmx.config.mode`](https://four.htmx.org/reference/config/htmx-config-mode)),
`settlingClass`, `swappingClass`, `triggerSpecsCache`, `useTemplateFragments`, `withCredentials` (use [
`hx-config`](https://four.htmx.org/reference/attributes/hx-config)), `wsBinaryType`, `wsReconnectDelay`
The `htmx-swapping`, `htmx-settling`, and `htmx-added` CSS classes are still applied during swaps. The config keys to
customize their names have been removed.
### Request headers
| htmx 2.x | htmx 4.x | Notes |
|-------------------|---------------------------------------------------------|------------------------------------------------------------------------|
| `HX-Trigger` | [`HX-Source`](https://four.htmx.org/reference/headers/HX-Source) | Format changed to `tagName#id` (e.g. `button#submit`) |
| `HX-Target` | [`HX-Target`](https://four.htmx.org/reference/headers/HX-Target) | Format changed to `tagName#id` |
| `HX-Trigger-Name` | removed | Use [`HX-Source`](https://four.htmx.org/reference/headers/HX-Source) |
| `HX-Prompt` | removed | Use [`hx-confirm`](https://four.htmx.org/reference/attributes/hx-confirm) with `js:` prefix |
| *(new)* | [`HX-Request-Type`](https://four.htmx.org/reference/headers/HX-Request-Type) | `"full"` or `"partial"` |
| *(new)* | [`Accept`](https://four.htmx.org/reference/headers/Accept) | Now explicitly `text/html` |
### Response headers
Removed:
- `HX-Trigger-After-Swap`
- `HX-Trigger-After-Settle`
Use [`HX-Trigger`](https://four.htmx.org/reference/headers/HX-Trigger) or JavaScript instead.
Unchanged: [`HX-Trigger`](https://four.htmx.org/reference/headers/HX-Trigger), [`HX-Location`](https://four.htmx.org/reference/headers/HX-Location), [
`HX-Push-Url`](https://four.htmx.org/reference/headers/HX-Push-Url), [`HX-Redirect`](https://four.htmx.org/reference/headers/HX-Redirect), [
`HX-Refresh`](https://four.htmx.org/reference/headers/HX-Refresh), [`HX-Replace-Url`](https://four.htmx.org/reference/headers/HX-Replace-Url), `HX-Retarget`,
`HX-Reswap`, `HX-Reselect`.
### JavaScript API changes
**Removed methods.** Use native JavaScript:
| htmx 2.x | Use instead |
|----------------------|------------------------------------------------------------------------|
| `htmx.addClass()` | `element.classList.add()` |
| `htmx.removeClass()` | `element.classList.remove()` |
| `htmx.toggleClass()` | `element.classList.toggle()` |
| `htmx.closest()` | `element.closest()` |
| `htmx.remove()` | `element.remove()` |
| `htmx.off()` | `removeEventListener()` (`htmx.on()` returns the callback) |
| `htmx.location()` | `htmx.ajax()` |
| `htmx.logAll()` | [`htmx.config.logAll`](https://four.htmx.org/reference/config/htmx-config-logAll) `= true` |
| `htmx.logNone()` | [`htmx.config.logAll`](https://four.htmx.org/reference/config/htmx-config-logAll) `= false` |
**Renamed:** `htmx.defineExtension()` is now `htmx.registerExtension()`.
**Still available:** `htmx.ajax()`, `htmx.config`, `htmx.find()`, `htmx.findAll()`, `htmx.on()`, `htmx.onLoad()`,
`htmx.parseInterval()`, `htmx.process()`, `htmx.swap()`, `htmx.trigger()`.
Note: `htmx.onLoad()` now listens on [`htmx:after:process`](https://four.htmx.org/reference/events/htmx-after-process), not [
`htmx:after:init`](https://four.htmx.org/reference/events/htmx-after-init).
## What's New
### Attributes
| Attribute | Purpose |
|----------------------------------------------------|-----------------------------------------------------------------------|
| [`hx-action`](https://four.htmx.org/reference/attributes/hx-action) | Specify URL (use with [`hx-method`](https://four.htmx.org/reference/attributes/hx-method)) |
| [`hx-method`](https://four.htmx.org/reference/attributes/hx-method) | Specify HTTP method |
| [`hx-config`](https://four.htmx.org/reference/attributes/hx-config) | Per-element request config (JSON or `key:value` syntax) |
| [`hx-ignore`](https://four.htmx.org/reference/attributes/hx-ignore) | Disable htmx processing (was `hx-disable`) |
| [`hx-validate`](https://four.htmx.org/reference/attributes/hx-validate) | Control form validation behavior |
### [`hx-swap`](https://four.htmx.org/reference/attributes/hx-swap) styles
```html
<div hx-get="/data" hx-swap="innerMorph">...</div>
<div hx-get="/data" hx-swap="outerMorph">...</div>
<div hx-get="/text" hx-swap="textContent">...</div>
<div hx-get="/remove" hx-swap="delete">...</div>
```
- `innerMorph` / `outerMorph`: morph swaps using the idiomorph algorithm. Better for preserving state in complex UIs.
- `textContent`: set the target's text content (no HTML parsing).
- `delete`: remove the target element entirely.
New aliases for existing swap styles (both old and new names work):
| New | Equivalent to |
|-----------|---------------|
| `before` | `beforebegin` |
| `after` | `afterend` |
| `prepend` | `afterbegin` |
| `append` | `beforeend` |
### [Status code swaps](https://four.htmx.org/reference/attributes/hx-status)
Set different swap behavior per HTTP status code:
```html
<form hx-post="/save"
hx-status:422="swap:innerHTML target:#errors select:#validation-errors"
hx-status:5xx="swap:none push:false">
<!-- form fields -->
</form>
```
Available config keys: `swap:`, `target:`, `select:`, `push:`, `replace:`, `transition:`.
Supports exact codes (`404`), single-digit wildcards (`50x`), and range wildcards (`5xx`). Evaluated in order of
specificity.
### `<hx-partial>`
Target multiple elements from one response:
```html
<hx-partial hx-target="#messages" hx-swap="beforeend">
<div>New message</div>
</hx-partial>
<hx-partial hx-target="#count">
<span>5</span>
</hx-partial>
```
Each `<hx-partial>` specifies its own [`hx-target`](https://four.htmx.org/reference/attributes/hx-target) and [
`hx-swap`](https://four.htmx.org/reference/attributes/hx-swap) strategy. A cleaner alternative to out-of-band swaps.
### Etag support
htmx 4 supports Etag-based conditional requests automatically:
- Response includes an [`Etag`](https://four.htmx.org/reference/headers/ETag) header: htmx stores it on the source element
- Next request from that element includes an [`If-None-Match`](https://four.htmx.org/reference/headers/If-None-Match) header
- `304 Not Modified` responses do not swap, avoiding unnecessary DOM updates
### View transitions
[View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) support is available but
disabled by default.
Enable: [`htmx.config.transitions`](https://four.htmx.org/reference/config/htmx-config-transitions) `= true`
### JSX compatibility
Frameworks that don't support `:` in attribute names can use [
`metaCharacter`](https://four.htmx.org/reference/config/htmx-config-metaCharacter) to replace it:
```js
htmx.config.metaCharacter = "-";
// hx-ws-connect instead of hx-ws:connect
// hx-confirm-inherited instead of hx-confirm:inherited
```
### JavaScript methods
- `htmx.forEvent(eventName, timeout)`: returns a promise that resolves when an event fires
- `htmx.takeClass(element, className, container)`: removes class from siblings, adds to element
- `htmx.timeout(time)`: returns a promise that resolves after a delay
### Request context
All events provide a consistent `ctx` object with request/response information.
### Events
| Event | Fires |
|------------------------------------------------------------------------------|---------------------------------------------------|
| [`htmx:after:cleanup`](https://four.htmx.org/reference/events/htmx-after-cleanup) | After element cleanup |
| [`htmx:after:history:update`](https://four.htmx.org/reference/events/htmx-after-history-update) | After history update |
| [`htmx:after:process`](https://four.htmx.org/reference/events/htmx-after-process) | After element processing |
| [`htmx:before:response`](https://four.htmx.org/reference/events/htmx-before-response) | Before response body is read (cancellable) |
| [`htmx:before:settle`](https://four.htmx.org/reference/events/htmx-before-settle) | Before settle phase |
| [`htmx:after:settle`](https://four.htmx.org/reference/events/htmx-after-settle) | After settle phase |
| [`htmx:before:viewTransition`](https://four.htmx.org/reference/events/htmx-before-viewTransition) | Before a view transition starts (cancellable) |
| [`htmx:after:viewTransition`](https://four.htmx.org/reference/events/htmx-after-viewTransition) | After a view transition completes |
| [`htmx:finally:request`](https://four.htmx.org/reference/events/htmx-finally-request) | Always fires after a request (success or failure) |
### Config keys
| Config | Default | Purpose |
|------------------------------------------------------------------------|-----------------|---------------------------------------------------------------|
| [`extensions`](https://four.htmx.org/reference/config/htmx-config-extensions) | `''` | Comma-separated list of allowed extension names |
| [`mode`](https://four.htmx.org/reference/config/htmx-config-mode) | `'same-origin'` | Fetch mode (replaces `selfRequestsOnly`) |
| [`inlineScriptNonce`](https://four.htmx.org/reference/config/htmx-config-inlineScriptNonce) | `''` | Nonce for inline scripts |
| [`inlineStyleNonce`](https://four.htmx.org/reference/config/htmx-config-inlineStyleNonce) | `''` | Nonce for inline styles |
| [`metaCharacter`](https://four.htmx.org/reference/config/htmx-config-metaCharacter) | `':'` | Separator character in attribute/event names |
| [`morphIgnore`](https://four.htmx.org/reference/config/htmx-config-morphIgnore) | `''` | CSS selector for elements to ignore during morph |
| [`morphScanLimit`](https://four.htmx.org/reference/config/htmx-config-morphScanLimit) | | Max elements to scan during morph matching |
| [`morphSkip`](https://four.htmx.org/reference/config/htmx-config-morphSkip) | `''` | CSS selector for elements to skip during morph |
| [`morphSkipChildren`](https://four.htmx.org/reference/config/htmx-config-morphSkipChildren) | `''` | CSS selector for elements whose children to skip during morph |
### SSE extension
The SSE extension uses `fetch()` and [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
instead of [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). This enables request bodies,
custom headers, and all HTTP methods.
See the [SSE extension documentation](https://four.htmx.org/docs/extensions/sse) for details.
### Core extensions
htmx 4 ships with 9 core extensions:
| Extension | Description |
|-----------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`alpine-compat`](https://four.htmx.org/docs/extensions/alpine-compat) | Alpine.js compatibility: initializes Alpine on fragments before swap |
| [`browser-indicator`](https://four.htmx.org/docs/extensions/browser-indicator) | Shows the browser's native loading indicator during requests |
| [`head-support`](https://four.htmx.org/docs/extensions/head-support) | Merges head tag information (styles, etc.) in htmx requests |
| [`htmx-2-compat`](https://four.htmx.org/docs/extensions/htmx-2-compat) | Restores implicit inheritance, old event names, and previous error-swapping defaults |
| [`optimistic`](https://four.htmx.org/docs/extensions/optimistic) | Shows expected content from a template before the server responds |
| [`preload`](https://four.htmx.org/docs/extensions/preload) | Triggers requests early (on mouseover/mousedown) for near-instant page loads |
| [`sse`](https://four.htmx.org/docs/extensions/sse) | Server-Sent Events streaming support |
| [`upsert`](https://four.htmx.org/docs/extensions/upsert) | Updates existing elements by ID and inserts new ones, preserving unmatched elements |
| [`ws`](https://four.htmx.org/docs/extensions/ws) | Bi-directional Web Socket communication |
## Checklist
1. Add config options or load [`htmx-2-compat`](https://four.htmx.org/docs/extensions/htmx-2-compat) for backward compatibility
2. Rename `hx-disable` to [`hx-ignore`](https://four.htmx.org/reference/attributes/hx-ignore), then `hx-disabled-elt` to [
`hx-disable`](https://four.htmx.org/reference/attributes/hx-disable)
3. Replace removed attributes with alternatives
4. Find/replace event names in JavaScript and `hx-on::` attributes
5. Replace removed API methods with native JS
6. Update extensions
7. Rename changed config keys
8. Test error handling (4xx/5xx now swap by default)
9. Test attribute inheritance
10. Test history navigation
## Migration Notes
Individual documentation pages include migration notes where features changed.
Look for these:
<details class="warning">
<summary>Changes in htmx 4.0</summary>
</details>
## Get Help
- [GitHub Discussions](https://github.com/bigskysoftware/htmx/discussions)
- [Discord](https://htmx.org/discord)
- [Examples](https://four.htmx.org/examples)
htmx 4 swaps all HTTP responses. Only 204
and 304 do not swap.
htmx 2 did not swap 4xx and 5xx responses. In htmx 4, if your server returns HTML with a 422 or 500, that HTML
gets swapped into the target. Design your error responses to work as swap content, or use hx-status to control per-code behavior.
In htmx 2, out-of-band (hx-swap-oob) elements swapped before the main
content.
In htmx 4, the main content swaps first. OOB and hx-partial elements swap after (in document order).
This matters if an OOB swap creates or modifies DOM that the main swap depends on. If your app relies on that ordering,
restructure so each swap is independent.
The double-colon shorthand for htmx events no longer works. Because event names changed from camelCase to
colon-separated (e.g. htmx:afterRequest → htmx:after:request), the hx-on:: prefix can no longer map to the correct
event name.
This applies to all hx-on:: event handlers. Find and replace hx-on:: with hx-on:htmx: and update the event name to
the new colon-separated format (see table above).
The htmx-swapping, htmx-settling, and htmx-added CSS classes are still applied during swaps. The config keys to
customize their names have been removed.