HTMX Extensions

HTMX's core library is intentionally minimal. It handles the essential request-response cycle but delegates specialized functionality to extensions. Extensions are separate JavaScript files that add new attributes, behaviors, and capabilities to HTMX without bloating the core. This topic covers the most useful official extensions and shows you how to load and use them.

How Extensions Work

Extensions register themselves with HTMX when their script loads. Once registered, they listen for HTMX events and add new attribute-driven behaviors. You activate an extension on an element using hx-ext="extension-name".

  Loading an extension:
  <script src="https://unpkg.com/htmx.org@2.0.0"></script>
  <script src="https://unpkg.com/htmx-ext-NAME/ext.js"></script>

  Using an extension:
  <div hx-ext="NAME">...</div>

  Extensions inherit down the DOM — putting hx-ext on body
  activates the extension for the whole page.

Official Extensions Overview

ExtensionPackageWhat It Adds
ssehtmx-ext-sseServer-Sent Events support
wshtmx-ext-wsWebSocket support
json-enchtmx-ext-json-encSends requests as JSON instead of form-encoded
preloadhtmx-ext-preloadPreloads linked pages on hover for near-instant navigation
class-toolshtmx-ext-class-toolsAdds, removes, or toggles CSS classes on elements over time
loading-stateshtmx-ext-loading-statesAdds detailed loading state management to any attribute
multi-swaphtmx-ext-multi-swapSwaps multiple parts of the page from one response
response-targetshtmx-ext-response-targetsRoutes different HTTP status codes to different targets
path-depshtmx-ext-path-depsAutomatically refreshes elements when a related path changes

json-enc: Sending JSON Requests

By default, HTMX sends form data as URL-encoded key-value pairs. When your API expects JSON, use the json-enc extension:

<script src="https://unpkg.com/htmx-ext-json-enc@2.0.1/json-enc.js"></script>

<form
  hx-ext="json-enc"
  hx-post="/api/users"
  hx-target="#result">
  <input type="text" name="username" placeholder="Username">
  <input type="email" name="email" placeholder="Email">
  <button type="submit">Create User</button>
</form>

Without the extension, HTMX sends: username=alice&email=alice@example.com

With json-enc, HTMX sends: {"username":"alice","email":"alice@example.com"}

The server receives a proper JSON body and the Content-Type header is set to application/json automatically.

preload: Near-Instant Navigation

<script src="https://unpkg.com/htmx-ext-preload@2.0.1/preload.js"></script>

<body hx-ext="preload" hx-boost="true">
  <nav>
    <a href="/home" preload="mouseover">Home</a>
    <a href="/products" preload="mouseover">Products</a>
    <a href="/about" preload="mouseover">About</a>
  </nav>
</body>
  Flow:

  User moves mouse over "Products" link
          ↓
  preload fires GET /products in the background (hidden)
          ↓
  User clicks the link (200ms later)
          ↓
  Response is already cached → page appears instantly

response-targets: Route Errors to Different Elements

The response-targets extension lets you send different HTTP status codes to different DOM targets. This is ideal for routing error responses to an error panel while successful responses go to the main content area:

<script src="https://unpkg.com/htmx-ext-response-targets@2.0.0/response-targets.js"></script>

<form
  hx-ext="response-targets"
  hx-post="/submit"
  hx-target="#success-panel"
  hx-target-422="#error-panel"
  hx-target-5*="#server-error-banner">
  <input type="text" name="title" required>
  <button type="submit">Submit</button>
</form>

<div id="success-panel"></div>
<div id="error-panel"></div>
<div id="server-error-banner"></div>
Server ResponseTarget Used
200 OK#success-panel
422 Unprocessable#error-panel
500, 503, etc.#server-error-banner (wildcard 5*)

class-tools: CSS Class Scheduling

<script src="https://unpkg.com/htmx-ext-class-tools@2.0.1/class-tools.js"></script>

<!-- Add "open" class immediately, remove it after 3 seconds -->
<div
  hx-ext="class-tools"
  classes="add open:0ms, remove open:3000ms">
  This appears, then disappears.
</div>

<!-- Toggle "active" class every 1 second -->
<div
  hx-ext="class-tools"
  classes="toggle active:1000ms">
  Blinking element
</div>

This extension is useful for timed notifications, attention-grabbing animations, and entry/exit effects without JavaScript.

multi-swap: Update Multiple Targets From One Response

<script src="https://unpkg.com/htmx-ext-multi-swap@2.0.0/multi-swap.js"></script>

<button
  hx-ext="multi-swap"
  hx-post="/update-dashboard"
  hx-swap="multi:#stats:innerHTML,#chart:outerHTML,#alerts:beforeend">
  Refresh Dashboard
</button>

The response must contain elements with matching IDs. The extension routes each to its specified target and swap strategy in one operation — similar to OOB swaps but driven by the client's hx-swap attribute instead of the server's response markup.

Writing a Custom Extension

If no existing extension meets your needs, write your own:

<script>
htmx.defineExtension('my-logger', {
    onEvent: function(name, event) {
        if (name === 'htmx:beforeRequest') {
            console.log('[my-logger] Sending:', event.detail.requestConfig.path);
        }
        if (name === 'htmx:afterRequest') {
            console.log('[my-logger] Done:', event.detail.successful);
        }
    }
});
</script>

<body hx-ext="my-logger">
  ...all HTMX requests logged to console...
</body>

A custom extension listens to HTMX lifecycle events and runs code at each stage. Extensions can also add new attributes, intercept responses, or modify request configurations.

Key Takeaway

Extensions expand HTMX beyond its core feature set without bloating the base library. Load each extension as a separate script tag after the main HTMX script, then activate it with hx-ext="name" on a container element. Use json-enc for JSON APIs, preload for near-instant navigation, response-targets for error routing, and class-tools for timed CSS class changes. Build custom extensions when the built-in options do not cover a specific need — the extension API is straightforward and well-documented.

Leave a Comment

Your email address will not be published. Required fields are marked *