# How htmx Handles Out-of-Band (OOB) Swaps: A Technical Deep Dive

> Discover how htmx handles Out-of-Band (OOB) swaps with a technical deep dive. Learn about `hx-swap-oob`, `findAndSwapOobElements`, and event handling for seamless updates.

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

---

**TLDR:** When htmx receives a server response, it scans for elements marked with `hx-swap-oob`, processes them outside the normal swap flow using `findAndSwapOobElements`, and swaps them into arbitrary page targets while emitting `htmx:oobBeforeSwap` and `htmx:oobAfterSwap` events.

Out-of-band swapping allows a single server response to update multiple, unrelated DOM fragments without JavaScript. In the **bigskysoftware/htmx** repository, this capability is implemented in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js) through a dedicated extraction pipeline that isolates OOB elements from the main response content before applying standard swap mechanics.

## The OOB Processing Pipeline

The `findAndSwapOobElements` function orchestrates the entire workflow, transforming server responses into targeted DOM updates through several discrete stages.

### Fragment Creation and Element Detection

After an HTTP request completes, htmx converts the response text into a `DocumentFragment` via the `makeFragment` utility (lines 60-84). The system then scans this fragment for any nodes carrying the `hx-swap-oob` attribute (or its `data-hx-swap-oob` variant) using the selector query `findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')` (lines 57-60).

### Nested OOB Policy and Configuration

Each discovered OOB element undergoes a parentage validation check. According to the default configuration object defined at lines 70-74, an element is processed only if:

- The global config flag `allowNestedOobSwaps` is **true** (the default value), **or**
- The element has no parent (`oobElement.parentElement === null`)

This prevents inadvertently processing nested OOB elements unless explicitly enabled by the developer.

## Parsing OOB Values and Target Resolution

Inside the `oobSwap` function (lines 66-78), htmx interprets the attribute value to determine swap behavior:

- **`true`** → Performs an `outerHTML` swap targeting the element's own `id`
- **`swapStyle:selector`** → Uses the specified swap style (`innerHTML`, `outerHTML`, `beforebegin`, `afterend`, `delete`, etc.) against the provided CSS selector

The selector is resolved into actual DOM nodes via `querySelectorAllExt`. If no matching targets exist, htmx emits an `htmx:oobErrorNoTarget` event and the OOB element is discarded.

## Swap Execution and Event Lifecycle

Before modifying the DOM, htmx fires `htmx:oobBeforeSwap`, allowing event handlers to cancel the operation or retarget the swap dynamically. The actual content replacement uses the standard `swapWithStyle` pipeline, which respects registered extensions, inline swap specifications, and transition settings. Upon completion, `htmx:oobAfterSwap` is dispatched to signal success.

## Cleanup and Error Handling

The original OOB element is stripped from the response fragment by removing its `hx-swap-oob` attribute, ensuring it never appears in the main content swap. If target resolution fails, the element is simply discarded after triggering the error event, preventing partial UI updates.

## Practical OOB Swap Examples

### Basic Target Update

Replace an element elsewhere in the page by referencing its ID:

```html
<!-- Page markup -->
<div id="status">Loading…</div>
<button hx-get="/msg" hx-target="#status">Refresh</button>

<!-- Server response -->
<div id="status" hx-swap-oob="innerHTML">Ready</div>

```

### Custom Selector and Swap Style

Update specific elements using CSS selectors without ID constraints:

```html
<ul>
  <li class="item" id="i1">Old Item</li>
</ul>

<!-- Server response -->
<div hx-swap-oob="outerHTML:.item[data-key='i1']">New Item</div>

```

### Element Deletion

Remove nodes entirely using the `delete` swap style:

```html
<div id="toast" hx-swap-oob="delete">Dismiss Message</div>

```

### Listening to OOB Events

Monitor the OOB lifecycle for debugging or analytics:

```javascript
document.body.addEventListener('htmx:oobBeforeSwap', function (evt) {
  console.log('About to OOB-swap', evt.detail);
});

```

## Configuration Options

Control nested OOB processing globally via the `allowNestedOobSwaps` configuration flag:

```javascript
// Disable nested OOB processing (they will be stripped from responses)
htmx.config.allowNestedOobSwaps = false;

```

The comprehensive test suite in [`test/attributes/hx-swap-oob.js`](https://github.com/bigskysoftware/htmx/blob/main/test/attributes/hx-swap-oob.js) validates all swap styles, selector combinations, and error conditions, while the minified builds in [`dist/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/htmx.js) contain the identical OOB logic for production deployment.

## Summary

- **OOB elements** are detected via the `hx-swap-oob` attribute and processed by `findAndSwapOobElements` in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js)
- The **nested OOB policy** respects the `allowNestedOobSwaps` config flag (default `true`) to control whether child elements are processed
- **Target resolution** uses `querySelectorAllExt`, with `htmx:oobErrorNoTarget` firing on selector misses
- The **event lifecycle** includes `htmx:oobBeforeSwap` (cancellable) and `htmx:oobAfterSwap` (completion)
- OOB elements are **removed from the response fragment** after processing to prevent duplicate rendering

## Frequently Asked Questions

### What happens if an OOB selector matches no elements?

If `querySelectorAllExt` returns no matches for an OOB selector, htmx emits `htmx:oobErrorNoTarget` and discards the OOB element without modifying the DOM. The main response swap continues normally.

### Can I use OOB swaps inside the swapped content itself?

Yes, provided `allowNestedOobSwaps` remains `true`. When enabled, htmx processes OOB elements found anywhere in the response fragment, including those nested within the main content target. Set this config to `false` to strip nested OOB markers instead.

### What swap styles are supported for OOB elements?

According to the source in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js) (lines 66-78), OOB swaps support all standard htmx swap styles: `innerHTML`, `outerHTML`, `beforebegin`, `afterbegin`, `beforeend`, `afterend`, `delete`, and `none`. The syntax follows `hx-swap-oob="style:selector"` or simply `hx-swap-oob="true"` for outerHTML on the element's ID.

### How do I prevent an OOB swap from occurring?

Cancel the swap by listening for `htmx:oobBeforeSwap` and calling `evt.preventDefault()` in your handler. You can also modify `evt.detail.target` to retarget the swap to a different element before it executes.