htmx supports extensions to augment its core hypermedia infrastructure. The extension mechanism takes pressure off the core library to add new features, allowing it to focus on its main purpose of generalizing hypermedia controls.
For the catalog of core extensions shipped with htmx, see /extensions.
Using Extensions
In htmx 4, extensions hook into standard events rather than callback extension points. They are lightweight with no performance penalty.
Extensions apply page-wide without requiring hx-ext on parent elements. They activate via custom attributes where needed.
Loading an Extension
Include the extension script after htmx. Core extensions ship with htmx in the /ext/ directory:
<script src="https://cdn.jsdelivr.net/npm/htmx.org@next/dist/htmx.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/htmx.org@next/dist/ext/hx-sse.js"></script>
Or with a bundler:
import 'htmx.org'; import 'htmx.org/dist/ext/hx-sse';
Restricting Extensions
To restrict which extensions can register, use an allow list:
<meta name="htmx-config" content='{"extensions": "my-ext,another-ext"}'>
When this config is set, only the listed extensions will be loaded. Without it, all registered extensions are active.
Building Extensions
htmx 4 introduces an extension system based on event hooks rather than the old callback-based API.
Defining an Extension
Extensions are defined using htmx.registerExtension():
htmx.registerExtension("my-ext", { init: (internalAPI) => { // Called once when extension is registered // Store internalAPI reference if needed }, htmx_before_request: (elt, detail) => { // Called before each request // Return false to cancel }, htmx_after_request: (elt, detail) => { // Called after each request }, });
Event Hooks
Extensions hook into htmx lifecycle events. Event names use underscores instead of colons:
Core Lifecycle Events
| Hook Name | Triggered Event | Parameters | Description |
|---|---|---|---|
htmx_before_init | htmx:before:init | (elt, detail) | Before element initialization |
htmx_after_init | htmx:after:init | (elt, detail) | After element initialization |
htmx_before_process | htmx:before:process | (elt, detail) | Before processing element |
htmx_after_process | htmx:after:process | (elt, detail) | After processing element |
htmx_before_cleanup | htmx:before:cleanup | (elt, detail) | Before cleaning up element |
htmx_after_cleanup | htmx:after:cleanup | (elt, detail) | After cleaning up element |
Request Lifecycle Events
| Hook Name | Triggered Event | Parameters | Description |
|---|---|---|---|
htmx_config_request | htmx:config:request | (elt, detail) | Configure request before sending |
htmx_before_request | htmx:before:request | (elt, detail) | Before request is sent |
htmx_before_response | htmx:before:response | (elt, detail) | After fetch, before body consumed |
htmx_after_request | htmx:after:request | (elt, detail) | After request completes |
htmx_finally_request | htmx:finally:request | (elt, detail) | Always called after request |
htmx_error | htmx:error | (elt, detail) | On request error |
Swap Events
| Hook Name | Triggered Event | Parameters | Description |
|---|---|---|---|
htmx_before_swap | htmx:before:swap | (elt, detail) | Before content swap |
htmx_after_swap | htmx:after:swap | (elt, detail) | After content swap |
htmx_before_settle | htmx:before:settle | (elt, detail) | Before settle phase |
htmx_after_settle | htmx:after:settle | (elt, detail) | After settle phase |
handle_swap | (direct call) | (swapStyle, target, fragment, swapSpec) | Custom swap handler |
History Events
| Hook Name | Triggered Event | Parameters | Description |
|---|---|---|---|
htmx_before_history_update | htmx:before:history:update | (elt, detail) | Before updating history |
htmx_after_history_update | htmx:after:history:update | (elt, detail) | After updating history |
htmx_after_history_push | htmx:after:history:push | (elt, detail) | After pushing to history |
htmx_after_history_replace | htmx:after:history:replace | (elt, detail) | After replacing history |
htmx_before_history_restore | htmx:before:history:restore | (elt, detail) | Before restoring from history |
Cancelling Events
Return false or set detail.cancelled = true to cancel an event:
htmx.registerExtension("validator", { htmx_before_request: (elt, detail) => { if (!isValid(detail.ctx)) { return false; // Cancel request } }, });
Internal API
The init hook receives an internal API object with helper methods:
let api; htmx.registerExtension("my-ext", { init: (internalAPI) => { api = internalAPI; }, htmx_after_init: (elt) => { let value = api.attributeValue(elt, "hx-my-attr"); let specs = api.parseTriggerSpecs("click, keyup delay:500ms"); let { method, action } = api.determineMethodAndAction(elt, evt); }, });
Available internal API methods:
attributeValue(elt, name, defaultVal, returnElt)- Get htmx attribute value with inheritanceparseTriggerSpecs(spec)- Parse trigger specification stringdetermineMethodAndAction(elt, evt)- Get HTTP method and URLcreateRequestContext(elt, evt)- Create request context objectcollectFormData(elt, form, submitter)- Collect form datahandleHxVals(elt, body)- Processhx-valsattribute
Request Context
The detail.ctx object contains request information:
{ sourceElement, // Element triggering request sourceEvent, // Event that triggered request status, // Request status target, // Target element for swap swap, // Swap strategy request: { action, // Request URL method, // HTTP method headers, // Request headers body, // Request body (FormData) validate, // Whether to validate abort, // Function to abort request signal // AbortSignal }, response: { // Available after request raw, // Raw Response object status, // HTTP status code headers // Response headers }, text, // Response text (after request) hx // HX-* response headers (parsed) }
Custom Swap Strategies
Extensions can implement custom swap strategies:
htmx.registerExtension("my-swap", { handle_swap: (swapStyle, target, fragment, swapSpec) => { if (swapStyle === "my-custom-swap") { target.appendChild(fragment); return true; // Handled } return false; // Not handled }, });
For migrating extensions written for htmx 2.x, see Migration → Migrating Your Own Extensions.