Updating Two Parts of a Page with One htmx Request: hx-swap-oob in Practice
htmx normally swaps one target per request. hx-swap-oob lets a single response update several page regions at once — here's how it works, with a like-button example and the failure modes to watch for.
17 Aug 2025, 23:41 UTC

You click a "Like" button. The like count needs to change — but so does the notification badge in the header, and maybe a "liked by you" indicator somewhere else on the page. With plain htmx, one request swaps one target. So you either fire extra requests, return a bloated fragment that re-renders half the page, or start wiring up custom JavaScript. There's a fourth option built into htmx: hx-swap-oob, which lets a single response carry extra fragments that update other elements "out of band."
The thesis is simple: if your server already knows everything that changed, it should say so in one response. Out-of-band swaps are how you do that without giving up htmx's declarative model.
How out-of-band swaps actually work
A normal htmx request takes the response HTML and swaps it into the element named by hx-target. An out-of-band (OOB) swap adds a rule: any element in the response that carries an hx-swap-oob attribute is pulled out of the main swap and instead matched — by id — against an element already on the page, which it replaces.
Two things follow from that design:
- The matching is by
id, so every OOB fragment must have anidthat exists in the current page. - The OOB fragment is removed from the content that lands in the primary target. It doesn't appear twice.
The attribute value is a swap spec, the same mini-language hx-swap uses: hx-swap-oob="true" means "use the default swap" (which is outerHTML for OOB content), while hx-swap-oob="innerHTML" or hx-swap-oob="beforeend:#list" give you control over how and where the content lands.
A worked example: like button plus notification badge
Assume htmx 1.9.x or 2.x loaded via a script tag, and a server that renders HTML fragments (any stack works — the example below is framework-agnostic).
The page:
<button hx-post="/posts/42/like"
hx-target="#like-count"
hx-swap="innerHTML">
Like
</button>
<span id="like-count">12</span>
<!-- elsewhere, e.g. in the site header -->
<span id="notification-badge">3</span>The server endpoint POST /posts/42/like records the like, then returns one response containing both updates:
<span id="like-count">13</span>
<span id="notification-badge" hx-swap-oob="innerHTML">4</span>htmx processes this as: the first span goes into #like-count (the primary target, using innerHTML as declared on the button). The second span is intercepted because of hx-swap-oob, matched to the existing #notification-badge by id, and its content swapped in. One request, two updated regions, zero JavaScript.
Run this anywhere you can serve two routes — the static page and the POST endpoint. No special permissions needed. To verify, open the browser's Network tab: you should see exactly one POST per click, and the response body should contain both fragments. Then inspect the DOM to confirm the badge changed even though it was never the target.
Where this earns its keep — and where it doesn't
OOB swaps shine when a single action has a small, known set of side effects: a cart icon count, a flash message area, a "saved" indicator. The common pattern is to reserve two or three fixed regions of the page (a header badge, a toast container) that any endpoint can update opportunistically by tacking an extra fragment onto its normal response.
The trade-offs are real, though:
- Silent failure on id mismatch. If the OOB fragment's id doesn't exist on the page, htmx simply skips it. No error, no console warning in most versions. When an update "doesn't happen," check the id first.
- Server-side coupling. The endpoint now needs to know which regions of the page exist. That's fine for a badge, awkward when five endpoints each emit different OOB fragments for a page that might be in different states.
- Sanitization is on you. htmx inserts the response HTML into the DOM as-is. If any part of the fragment includes user-generated content, escape it on the server exactly as you would for a normal template render.
- Diminishing returns at scale. If the OOB payload grows to half the page, a full-fragment swap (or
hx-boostnavigation) is simpler and easier to reason about.
A practical way to adopt it
Start small: pick one page where a single action visibly desynchronizes two UI elements, convert the secondary update to an OOB fragment, and verify in the Network tab that request count dropped without breaking the primary swap. If you find yourself adding a fourth or fifth OOB fragment to one response, that's the signal to step back and reconsider the fragment boundaries — the feature is a scalpel, not a page-rendering strategy.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.