Changes in htmx 4.0
htmx 4.0 is a ground up rewrite of the implementation of htmx, using the fetch() API. This document outlines the
major changes between htmx 2.x and htmx 4.x.
If you are upgrading from htmx 2.x, see the htmx 2.x → 4.x Migration Guide for step-by-step instructions, including attribute renames, removed attributes, event name mappings, and HTTP header changes.
Major Changes
fetch() API replaces XMLHttpRequest
All AJAX requests now use the native fetch() API instead of XMLHttpRequest.
- Enables streaming response support
- Simplifies implementation of htmx significantly
Explicit Attribute Inheritance
Attribute inheritance is now explicit by default, using the :inherited modifier (e.g. hx-target:inherited="value").
This improves locality of behavior by making inheritance visible in the markup.
- Applies to all inheritable attributes:
hx-boost:inherited,hx-target:inherited,hx-confirm:inherited, etc. - You can revert to implicit inheritance by setting
htmx.config.implicitInheritancetotrue
Event Naming Convention Changed
Events now use a colon-separated naming convention: htmx:phase:action[:sub-action].
- This provides more consistent & predictable event naming
- Many event names have changed (see the migration guide for the full mapping table)
History Storage
History no longer uses localStorage to store snapshots of previous pages.
- History now issues a full page refresh request on history navigation
- This is a much, much more reliable history restoration mechanic
Non-200 Swapping Defaults
In htmx 4.0, all responses will swap by default, not just 2xx responses.
- Only
204 - No Contentand304 - Not Modifieddo not swap - You can revert to not swapping
4xxand5xxresponses by settinghtmx.config.noSwapto[204, 304, '4xx', '5xx']
GET/DELETE Form Data
Like hx-get, hx-delete does not include the enclosing form’s
inputs by default. Use hx-include="closest form" if you need this behavior.
Out-of-Band Swap Order
Out-of-band (hx-swap-oob) and partial swaps now happen after the main content swap,
rather than before as in htmx 2.
Default Timeout
htmx 4 sets a default request timeout of 60 seconds (htmx.config.defaultTimeout = 60000).
- In htmx 2, the default timeout was
0(no timeout), meaning requests could hang indefinitely - Set
htmx.config.defaultTimeoutto0to restore the old behavior
Extension Loading
Extensions no longer use the hx-ext attribute. They are activated by including the script file and approving them
via config:
<meta name="htmx-config" content='{"extensions": "sse, ws"}'>
<script src="/path/to/htmx.js"></script>
<script src="/path/to/ext/hx-sse.js"></script>
New Features
Morphing Swap
htmx now ships with morph swap styles based on the original idiomorph algorithm.
innerMorph- morphs the children of the target elementouterMorph- morphs the target element itself- Does a better job of preserving local state when targeting large DOM trees
hx-config Attribute
The new hx-config attribute allows per-element request configuration.
- Accepts JSON or key:value syntax
- Example:
hx-config='{"timeout": 5000}'orhx-config="timeout:5000"
HTTP Status Code Conditional Swapping
The new hx-status:XXX attribute pattern allows different swap behaviors based on HTTP response status.
- Supports exact codes (
hx-status:404) and wildcards (hx-status:5xx,hx-status:50x) - Value uses config syntax to set context properties:
target:,swap:,select:, etc. - Example:
hx-status:422="select:#errors target:#error-container" - Example:
hx-status:5xx="swap:none"
hx-partial Tags
The new <hx-partial> tag allows multiple targeted swaps in a single response, providing a cleaner
alternative to out-of-band swaps.
- Each partial specifies its own target via
hx-targetand swap strategy viahx-swap - Example:
<hx-partial hx-target="#messages" hx-swap="beforeend"> <div>New message</div> </hx-partial> <hx-partial hx-target="#notifications"> <span class="badge">5</span> </hx-partial>
hx-action and hx-method
The hx-action and hx-method attributes have been added alongside
hx-get for people who want an API more consistent with the existing browser attributes
Modern Swap Terminology
New modern swap style names are supported alongside the traditional names:
before(equivalent tobeforebegin)after(equivalent toafterend)prepend(equivalent toafterbegin)append(equivalent tobeforeend)- Both old and new terminology work (backward compatible)
- Example:
hx-swap="prepend"works the same ashx-swap="afterbegin"
Inheritance Attribute Modifiers
A new :append modifier for attributes can be used to append values to inherited values
- Values are comma-separated when appended
- Example:
hx-include:append=".child"appends.childto any inheritedhx-includevalue - Can be combined with
:inheritedfor chaining:hx-include:inherited:append=".parent" - Works with all htmx attributes that accept value lists
View Transitions
View Transitions API support is available but disabled by default.
- Provides smooth animated transitions between DOM states
- Set
htmx.config.transitions = trueto enable
Scripting API
New async helper methods make it easier to integrate custom JavaScript with htmx.
htmx.forEvent(eventName, timeout)- returns a promise that resolves when an event fireshtmx.timeout(time)- returns a promise that resolves after specified time
Unified Request Context
All events now provide a consistent ctx object with request/response information.
- Easier to access request/response information in event handlers
- More predictable event handling across the request lifecycle
Etag / Conditional Requests
htmx 4 has built-in support for Etag-based conditional requests.
- When a response includes an
Etagheader, htmx stores it on the source element - Subsequent requests from that element automatically include an
If-None-Matchheader 304 Not Modifiedresponses do not swap by default, avoiding unnecessary DOM updates
JSX-Compatible Attribute Names
The new metaCharacter config option allows replacing : in attribute names with a custom character for
frameworks that don’t support colons in attribute names.
- Example: setting
htmx.config.metaCharacter = "-"allowshx-ws-connectinstead ofhx-ws:connect - Useful for JSX/TSX, Vue, and other template systems with strict attribute name rules
Server-Sent Events Extension
SSE support is provided via the SSE extension, rewritten from scratch for htmx 4.
- Uses Fetch + ReadableStream instead of EventSource (supports POST, custom headers, cookies)
- Two modes: one-off streams via any
hx-*request returningtext/event-stream, persistent connections viahx-sse:connect
Core Extensions
htmx 4 ships with 9 core extensions:
| Name | Description |
|---|---|
| alpine-compat | Alpine.js compatibility — initializes Alpine on fragments before swap and preserves Alpine state during morph operations |
| browser-indicator | Shows the browser's native loading indicator (tab spinner) during htmx requests |
| head-support | Provides support for merging head tag information (styles, etc.) in htmx requests |
| htmx-2-compat | htmx 2.0 backwards compatibility — restores implicit inheritance, old event names, and previous error-swapping defaults |
| optimistic | Immediately shows expected content from a template before the server responds, then replaces it with the actual response on completion |
| preload | Triggers requests early (on mouseover or mousedown) so the response is cached by the time the user clicks, making pages appear to load nearly instantaneously |
| sse | Adds support for Server-Sent Events streaming to htmx |
| upsert | Adds an upsert swap strategy that updates existing elements by ID and inserts new ones, preserving elements not in the response |
| ws | Provides bi-directional communication with Web Sockets servers directly from HTML |
See the Extensions documentation for details.