hx-swap-oob: Updating Several Page Regions From One htmx Response
hx-swap-oob lets one htmx response update several regions of a page. Here is the syntax, a worked add-a-note example, and the silent failures to watch for.
05 May 2026, 18:41 UTC

The problem: one action, several regions of the page
You have a form that adds a note. Submitting it should append the note to a list, update a "4 notes" counter, and flash a confirmation toast. With plain htmx, one request swaps one target — the list. The counter and the toast stay stale.
The usual workarounds are unsatisfying: fire extra requests, or return a whole page and swap a large container, which throws away scroll position and focus. hx-swap-oob exists for this case. It lets a single response carry fragments for several targets, and htmx applies each one.
How an out-of-band fragment is marked up
An out-of-band element is a normal HTML element in the response carrying an hx-swap-oob attribute. htmx pulls it out of the response, then applies it to the element matched by the selector.
| Attribute value | What htmx does |
|---|---|
hx-swap-oob="true" | Uses the element's own id as the selector and swaps with outerHTML |
hx-swap-oob="outerHTML:#note-count" | Replaces the matched element entirely |
hx-swap-oob="innerHTML:#toast-holder" | Replaces only the matched element's children |
hx-swap-oob="beforeend:#note-list" | Appends the fragment inside the matched element |
hx-swap-oob="delete" | Removes the matched element |
The swap types are the same ones hx-swap accepts, so afterbegin, beforeend and afterend all work. The selector is a CSS selector, so #id, .class or [data-region="cart"] are valid.
A worked example: add a note
Page markup. The form posts to /notes and appends the returned fragment to the list.
<div id="note-count">3 notes</div>
<div id="toast-holder"></div>
<form hx-post="/notes" hx-target="#note-list" hx-swap="beforeend">
<input name="body" required>
<button type="submit">Add note</button>
</form>
<ul id="note-list"></ul>
The server returns one fragment holding the new list item plus two out-of-band elements:
<li>Buy oat milk</li>
<div hx-swap-oob="outerHTML:#note-count">4 notes</div>
<div hx-swap-oob="innerHTML:#toast-holder">
<div class="toast">Note saved</div>
</div>
htmx appends the <li> to #note-list, replaces #note-count, and fills #toast-holder. No extra requests, no full-page swap, and the input keeps focus.
Note that out-of-band elements are carriers: the second one has no id of its own. Only the selector inside the attribute matters.
Where it bites
A missing selector fails silently
If #note-count is not in the DOM when the response is processed, htmx has nothing to swap into and the fragment is dropped. Nothing appears in the console by default. This is the most common way an out-of-band update "just stops working" after a template refactor.
The same thing happens when the out-of-band target sits inside the region the main swap replaces. If your main target is #note-list and the counter lives inside #note-list, the counter can be overwritten by the main swap after the out-of-band update lands. Keep out-of-band targets outside the main swap's subtree.
The response now knows your DOM
Out-of-band swaps move layout knowledge to the server: the backend emits #note-count as a string. Rename that id in a template and the server-side fragment breaks quietly. That coupling is the real cost of the pattern — reasonable for a handful of stable regions, painful once it spreads to dozens of one-off ids.
Everything you swap is trusted markup
htmx inserts the fragment into the DOM; it is not a sanitizer. Any user-supplied value reaching an out-of-band fragment must be escaped on the server exactly as it would be for a normal page render. Treat hx-swap-oob as markup delivery, not as a safety boundary.
Version notes worth confirming
In htmx 1.x, out-of-band elements generally need to be top-level children of the response. htmx 2.x processes nested ones by default, controlled by a configuration flag. Check the configuration reference for the version you have installed rather than assuming either behavior.
Verifying an out-of-band swap
- Open DevTools → Network, submit the form, and inspect the raw response. Confirm the
hx-swap-oobattribute is present and the selector matches an element that exists at that moment. - In the Elements panel, watch the target node. It should be replaced, or have its children replaced, without a document reload.
- Run
htmx.logAll()in the console before reproducing the action. It logs the swap lifecycle, which shows whether the out-of-band element was processed at all. htmx also fires an event when an out-of-band target cannot be found — check your version's event reference for the exact name. - Add a regression check: a test asserting that the selector string used by the server fragment still exists in the rendered page. That catches the silent-failure case at build time instead of in production.
When not to reach for it
If the only extra thing you need is a transient toast, an out-of-band swap is heavier than necessary. htmx can return an HX-Trigger response header naming a client-side event; a small listener shows the toast and the server never has to know about #toast-holder. Reserve hx-swap-oob for regions whose content genuinely comes from the server — counters, totals, badges, other lists.
Start with one out-of-band target, verify it in DevTools, and add more only when the response is already being generated for that request. The pattern is cheap per fragment and expensive per id.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.