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.
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
:inheritedmodifier - By default, in htmx 4.x you now use
hx-attribute:inherited="value"syntax to inherit an attribute - This applies to all inheritable attributes:
hx-boost:inherited,hx-target:inherited,hx-confirm:inherited, etc. - This improves locality of behavior by making inheritance explicit
- You can revert to implicit inheritance by setting
htmx.config.implicitInheritancetotrue
Event Naming Convention Changed
- New event naming convention:
htmx:phase:action[:sub-action](colon-separated) - Many event names have changed (See the migration guide)
- This provides more consistent & predictable event naming
History Storage
- History no longer uses
localStorageto 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
- We will be creating a caching history extension for people that want the old behavior
Non-200 Swapping Defaults
- In htmx 2.0, responses with
4xxand5xxresponse codes did not swap by default - In htmx 4.0, all responses will swap except for
204 - No Contentand304 - Not Modified - You can revert to not swapping
4xxand5xxresponses by settinghtmx.config.noSwapto[204, 304, '4xx', '5xx']
New Features
Morphing Swap
- htmx now ships with morph swap styles are now available, based on the original
idiomorphalgorithm 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
Built-in Streaming Response Support
- Streaming functionality/SSE now built into core htmx
- Improved event handling and reconnection logic
- Configure globally using
<meta name="htmx-config"><!-- Global defaults --> <meta name="htmx-config" content="{ streams:{ reconnect: false, reconnectDelay: 500, reconnectMaxDelay: 60000, reconnectMaxAttempts: 10, reconnectJitter: 0.3, closeOnHide: false } }"> - Or per-element using
hx-configattribute<!-- Overrides global default --> <div hx-get="/events" hx-trigger="load" hx-config="{stream: {reconnect: true}}"
View Transitions
- View Transitions API enabled by default (maybe not!)
- Provides smooth animated transitions between DOM states
- Set
htmx.config.transitions = falseto disable
Scripting API
- New unified scripting API for async operations
- Better integration points for custom JavaScript
- Improved support for async/await patterns
Unified Request Context
- All events now provide consistent
ctxobject - Easier to access request/response information
- More predictable event handling
Modern Swap Terminology
- New modern swap style names supported alongside classic 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
- New
:appendmodifier for attributes 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
HTTP Status Code Conditional Swapping
- New
hx-status:XXXattribute pattern for status-specific swap behaviors - Allows different swap strategies based on HTTP response status
- Supports exact codes:
hx-status:404="none" - Supports wildcards:
hx-status:2xx="innerHTML",hx-status:5xx="#error" - Example:
hx-status:404="#not-found"swaps into different target on 404 - Overrides default swap behavior when status code matches
Partial Tags
- New
<hx-partial>tag for multiple targeted swaps in one response - Provides explicit control over swap targets via
hx-targetattribute - Alternative to out-of-band swaps when you want explicit targeting
- 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> - Each partial specifies its own target and swap strategy
- More explicit than OOB swaps which rely on matching
idattributes
Attribute Changes
Renamed Attributes
hx-disablerenamed tohx-ignorehx-disabled-eltrenamed tohx-disable:/
Removed Attributes
hx-vars- usehx-valswithjs:prefix insteadhx-params- usehtmx:config:requestevent to filter parametershx-prompt- usehx-confirmwith async JavaScript functionhx-ext- extensions now work via event listenershx-disinherit- no longer needed (inheritance is explicit)hx-inherit- no longer needed (inheritance is explicit)hx-request- usehx-configinsteadhx-history- removed (history no longer uses local storage)hx-history-elt- removed (history uses target element)
New Attributes
hx-action- specifies URL for requests (use withhx-method)hx-method- specifies HTTP method (use withhx-action)hx-config- configure request behavior using JSONhx-status:XXX- conditional swap behavior based on HTTP status code (e.g.,hx-status:404="none")hx-ignore- replaces htmx 2.xhx-disablefor disabling htmx processing
Attribute Modifier Syntax
:inherited- explicitly inherit attribute value from parent (e.g.,hx-target:inherited="this"):append- append value to inherited value (e.g.,hx-include:append=".child"):inherited:append- combine inheritance and appending (e.g.,hx-vals:inherited:append='{"key":"value"}')
Event Changes
Event Name Mappings
htmx:afterOnLoad→htmx:after:inithtmx:afterProcessNode→htmx:after:inithtmx:afterRequest→htmx:after:requesthtmx:afterSettle→htmx:after:swaphtmx:afterSwap→htmx:after:swaphtmx:beforeCleanupElement→htmx:before:cleanuphtmx:beforeHistorySave→htmx:before:history:updatehtmx:beforeOnLoad→htmx:before:inithtmx:beforeProcessNode→htmx:before:inithtmx:beforeRequest→htmx:before:requesthtmx:beforeSwap→htmx:before:swaphtmx:configRequest→htmx:config:requesthtmx:historyCacheMiss→htmx:before:restore:historyhtmx:historyRestore→htmx:after:restore:historyhtmx:load→htmx:after:inithtmx:oobAfterSwap→htmx:after:oob:swaphtmx:oobBeforeSwap→htmx:before:oob:swaphtmx:pushedIntoHistory→htmx:after:push:into:historyhtmx:replacedInHistory→htmx:after:replace:into:historyhtmx:responseError→htmx:errorhtmx:sendError→htmx:errorhtmx:swapError→htmx:errorhtmx:targetError→htmx:errorhtmx:timeout→htmx:error
New Events
htmx:after:cleanup- fires after element cleanup completeshtmx:after:history:update- fires after history state is updatedhtmx:after:process- fires after element processing completeshtmx:before:settle- fires before settle phase beginshtmx:after:settle- fires after settle phase completeshtmx:finally:request- fires in finally block after request (success or error)htmx:before:sse:stream- fires before SSE stream beginshtmx:after:sse:stream- fires after SSE stream endshtmx:before:sse:message- fires before processing SSE messagehtmx:after:sse:message- fires after processing SSE messagehtmx:before:sse:reconnect- fires before attempting SSE reconnectionhtmx:before:viewTransition- fires before view transition startshtmx:after:viewTransition- fires after view transition completes
Extensions Are Now Globally Registered
- Extensions no longer require an explicit
hx-extattribute - Simpler extension architecture