HTMX Out of Band Swaps
A standard HTMX request updates one target element. Out of Band (OOB) swaps break that limit. With a single server response, HTMX can update multiple elements anywhere on the page — simultaneously and independently. This is one of the most powerful HTMX features for building complex, interconnected UIs without multiple round-trips to the server.
The Problem OOB Solves
Scenario: User posts a comment
─────────────────────────────
You want to update THREE things at once:
1. Append the new comment to the comment list
2. Update the comment count in the header ("47 Comments" → "48 Comments")
3. Show a success notification
Without OOB: 3 separate requests needed
With OOB: 1 request returns all three updates
How OOB Works
The server includes extra HTML elements in the response body, each tagged with the attribute hx-swap-oob="true". HTMX reads the response, routes the tagged elements to their matching DOM elements by ID, and swaps them independently — in addition to performing the main swap on the primary target.
Server response for POST /comments: ───────────────────────────────────── <!-- PRIMARY: goes into hx-target (the comment list) --> <li>Alice: Great post!</li> <!-- OOB 1: replaces the element with id="comment-count" --> <span id="comment-count" hx-swap-oob="true">48 Comments</span> <!-- OOB 2: replaces the element with id="toast" --> <div id="toast" hx-swap-oob="true">Comment posted!</div>
HTMX processes the response top to bottom. The first block (no OOB tag) goes into the primary target. The subsequent blocks (with hx-swap-oob="true") each find their matching DOM element by ID and replace it.
Full Example: Comment Form With OOB Updates
<!-- Comment form --> <form hx-post="/comments" hx-target="#comment-list" hx-swap="beforeend"> <textarea name="body"></textarea> <button type="submit">Post Comment</button> </form> <!-- Three areas that update simultaneously --> <h3>Comments (<span id="comment-count">47</span>)</h3> <ul id="comment-list"> <li>Existing comment one</li> <li>Existing comment two</li> </ul> <div id="toast" style="display:none"></div>
Server response (one response updates all three areas):
<!-- Primary swap: appended to #comment-list --> <li>Alice: Great post!</li> <!-- OOB: updates the comment count --> <span id="comment-count" hx-swap-oob="true">48</span> <!-- OOB: shows a toast notification --> <div id="toast" hx-swap-oob="true" style="display:block; color:green"> Comment posted successfully! </div>
Result: ┌─────────────────────────────────────────────┐ │ Comments (48) ← count updated │ │ │ │ • Existing comment one │ │ • Existing comment two │ │ • Alice: Great post! ← new comment │ │ │ │ [Comment posted! ✓] ← toast appeared │ └─────────────────────────────────────────────┘ All from ONE server response.
OOB Swap Strategies
By default, hx-swap-oob="true" uses outerHTML — it replaces the entire matching element. You can specify a different strategy:
<!-- Replace only the inner content of #sidebar --> <div id="sidebar" hx-swap-oob="innerHTML"> New sidebar content here </div> <!-- Append to #notifications list --> <li id="notifications" hx-swap-oob="beforeend"> You have a new message. </li>
OOB Without a Primary Swap
Sometimes you want only OOB updates — no primary target swap. Return only OOB elements and use hx-swap="none" on the triggering element:
<button hx-post="/mark-all-read" hx-swap="none"> Mark All Read </button>
Server returns only OOB elements:
<span id="unread-count" hx-swap-oob="true">0</span> <div id="inbox-status" hx-swap-oob="true">All caught up!</div>
The button itself does not change. Only the two OOB targets update.
OOB vs HX-Trigger Response Header
| Feature | OOB Swaps | HX-Trigger Header |
|---|---|---|
| What it does | Sends updated HTML directly | Fires an event; other elements fetch their own updates |
| Network requests | One total | One per listening element |
| Best for | Updates fully controlled by the server | Loosely coupled, independent components |
| Complexity | Server builds all HTML in one response | Each element fetches its own update independently |
Flask Example: Server Building OOB Response
from flask import render_template_string
@app.route('/comments', methods=['POST'])
def add_comment():
body = request.form.get('body')
comment = Comment(body=body)
db.session.add(comment)
db.session.commit()
count = Comment.query.count()
return render_template_string('''
<li>{{ body }}</li>
<span id="comment-count" hx-swap-oob="true">{{ count }}</span>
<div id="toast" hx-swap-oob="true" style="color:green">
Comment posted!
</div>
''', body=body, count=count)
Key Takeaway
Out of Band swaps let a single server response update multiple independent parts of the page. Tag extra HTML elements with hx-swap-oob="true" and give them the same id as their DOM counterparts. HTMX routes each tagged element to its matching DOM element automatically. Use OOB when the server knows exactly what all affected UI elements should show — it keeps the number of round-trips to one while keeping every related element in sync.
