# How to Use htmx Extensions: A Complete Guide to Customizing HTMX Behavior

> Master htmx extensions to customize behavior. Learn to register and activate extensions easily with this complete guide.

- Repository: [Big Sky Software/htmx](https://github.com/bigskysoftware/htmx)
- Tags: how-to-guide
- Published: 2026-08-30

---

**Register extensions using `htmx.defineExtension(name, ext)` and activate them on elements with the `hx-ext` attribute.**

htmx provides a modular extension system that lets you intercept requests, customize content swapping, and add new HTML attributes without modifying core library files. According to the bigskysoftware/htmx source code, the extension architecture centers around the `defineExtension` method located at approximately line 5004 of [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), which maintains an internal registry mapping extension names to configuration objects.

## Understanding the Extension Architecture

The htmx extension system comprises three core components working together during the request lifecycle.

**Extension Registry**. Located in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), the registry stores extension definitions added via `htmx.defineExtension(name, extensionObject)` and removed via `htmx.removeExtension(name)`. This registry resolves extension names to their implementation objects when htmx processes elements.

**Extension Loading**. When htmx encounters an element with the `hx-ext` attribute, the internal `withExtensions` function (lines 5116-5142 in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js)) walks the comma-separated list of extension names, retrieves each from the registry, and associates it with that element's request lifecycle.

**Hook Execution**. Extensions implement optional hooks such as `onEvent`, `isInlineSwap`, and `onSwap`. During request processing—specifically within functions like `handleSwap` and `triggerEvent`—htmx invokes these hooks at precise points (before requests, during swaps, after responses) to allow extensions to modify or cancel behavior.

## How to Register an Extension with defineExtension

Before using an extension, you must register it with the htmx runtime. The `htmx.defineExtension` method accepts two parameters: a string identifier and an object containing hook implementations.

```javascript
// Register a simple logging extension
htmx.defineExtension("log-events", {
  onEvent: function(name, evt) {
    console.log(`[htmx:${name}]`, evt);
  }
});

```

The extension object can implement any combination of these hooks:

- **`onEvent(name, evt)`**: Called for every htmx event (e.g., `htmx:beforeRequest`, `htmx:afterSwap`), receiving the canonical event name and native Event object
- **`isInlineSwap(swapStyle)`**: Returns boolean indicating whether this extension handles the specified swap style
- **`onSwap(swapStyle, target, fragment)`**: Performs the actual DOM manipulation for custom swap styles
- **`onBeforeRequest(config)`**: Modifies request configuration before the AJAX call
- **`onAfterSwap(target)`**: Executes cleanup or initialization after content insertion

## Activating Extensions with hx-ext

Once registered, activate extensions by adding the `hx-ext` attribute to any HTML element. Multiple extensions can be comma-separated.

```html
<!-- Single extension -->
<div hx-ext="log-events">
  <button hx-get="/api/data">Load Data</button>
</div>

<!-- Multiple extensions -->
<form hx-ext="json-enc, loading-states" hx-post="/api/submit">
  <input type="text" name="username">
  <button type="submit">Submit</button>
</form>

```

Extensions inherit to child elements unless overridden. Use `data-hx-ext` as an alternative if you require valid HTML5 data attributes.

## Complete Example: Custom Fade Swap Extension

Here is a practical implementation demonstrating custom swap handling and event interception:

```javascript
// Define custom fade behavior
htmx.defineExtension("fade-swap", {
  isInlineSwap: function(swapStyle) {
    return swapStyle === "fade";
  },
  
  onSwap: function(swapStyle, target, fragment) {
    if (swapStyle === "fade") {
      target.style.opacity = "0";
      target.innerHTML = "";
      target.appendChild(fragment);
      
      requestAnimationFrame(() => {
        target.style.transition = "opacity 0.5s ease-in-out";
        target.style.opacity = "1";
      });
    }
  },
  
  onEvent: function(name, evt) {
    if (name === "htmx:beforeSwap") {
      console.log("Starting fade transition...");
    }
  }
});

```

Usage in HTML:

```html
<div hx-get="/panel" hx-swap="fade" hx-ext="fade-swap">
  Content will fade in when updated
</div>

```

This extension registers the "fade" swap style (checked by `isInlineSwap`), implements the transition logic in `onSwap`, and logs lifecycle events via `onEvent`.

## Built-in Extensions Available in htmx

The bigskysoftware/htmx repository ships with several production-ready extensions located in `dist/ext/`:

**WebSocket Extension (`ws`)**. Enables real-time updates via WebSocket connections using the `hx-ws` attribute. Source: [`dist/ext/ws.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/ws.js), registers with `htmx.defineExtension("ws", ...)`.

**Server-Sent Events (`sse`)**. Supports unidirectional server push through `hx-sse` attributes. Source: [`dist/ext/sse.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/sse.js).

**JSON Encoding (`json-enc`)**. Automatically serializes form submissions as JSON when using `hx-encoding="json"`. Source: [`dist/ext/json-enc.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/json-enc.js).

**Method Override (`method-override`)**. Allows HTML forms to use HTTP verbs like PUT and DELETE via `hx-override-method`. Source: [`dist/ext/method-override.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/method-override.js).

**Loading States (`loading-states`)**. Automatically applies CSS classes such as `htmx-request` to elements during active requests. Source: [`dist/ext/loading-states.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/loading-states.js).

**Debug (`debug`)**. Logs every htmx event to the browser console for development troubleshooting. Source: [`dist/ext/debug.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/debug.js).

Include these extensions after the main htmx script:

```html
<script src="https://unpkg.com/htmx.org"></script>
<script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>

```

## Implementation Checklist for htmx Extensions

Follow these steps to implement any htmx extension correctly:

1. **Load htmx core** before defining extensions
2. **Define your extension object** with the required hooks (`onEvent`, `onSwap`, etc.)
3. **Register** the extension using `htmx.defineExtension('unique-name', extensionObject)`
4. **Attach** `hx-ext="unique-name"` to target elements in your HTML
5. **Test** the extension lifecycle by monitoring the specific hooks you implemented

Extensions defined later in the page lifecycle can still be applied to dynamically added content, as htmx processes `hx-ext` attributes during element initialization in the `withExtensions` pipeline.

## Summary

- **Use `htmx.defineExtension(name, config)`** (located at line ~5004 in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js)) to register extension objects with the htmx runtime
- **Activate extensions via `hx-ext`** attributes, supporting comma-separated lists for multiple extensions
- **Implement hooks** such as `onEvent`, `isInlineSwap`, and `onSwap` to intercept requests, define custom swap styles, and react to the htmx lifecycle
- **Reference built-in extensions** in `dist/ext/` for production patterns like WebSocket integration, JSON encoding, and loading state management
- **Process elements through `withExtensions`** (internal API, lines 5116-5142 in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js)) which resolves extension names and attaches them to the request pipeline

## Frequently Asked Questions

### What is the difference between hx-ext and data-hx-ext?

Both attributes activate htmx extensions, but `data-hx-ext` conforms to HTML5 data attribute specifications while `hx-ext` uses htmx's custom attribute syntax. The functionality is identical; htmx processes both variants when resolving extensions through the internal attribute parsing in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js).

### Can I use multiple htmx extensions on the same element?

Yes. Separate extension names with commas in the `hx-ext` attribute (e.g., `hx-ext="json-enc, loading-states"`). The `withExtensions` function in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js) (lines 5116-5142) iterates through the list and registers each extension's hooks for that element's lifecycle.

### When should I use isInlineSwap versus onSwap?

Use **`isInlineSwap(swapStyle)`** to declare that your extension handles a specific swap style (returning `true` for styles like "fade" or "morph"). Implement **`onSwap(swapStyle, target, fragment)`** to execute the actual DOM manipulation when that style is requested. The former acts as a capability check; the latter performs the work.

### Where are the built-in htmx extensions located?

Built-in extensions reside in the `dist/ext/` directory of the bigskysoftware/htmx repository. Each file (such as [`ws.js`](https://github.com/bigskysoftware/htmx/blob/main/ws.js), [`sse.js`](https://github.com/bigskysoftware/htmx/blob/main/sse.js), or [`json-enc.js`](https://github.com/bigskysoftware/htmx/blob/main/json-enc.js)) calls `htmx.defineExtension()` to self-register when loaded after the core htmx library.