← All demos

Fix: hx-swap-oob="textContent" falls back to defaultSwapStyle

Issue #3563 · Base: d53932d4 (dev)

Before Fix

upstream/dev @ d53932d4

swapWithStyle() has no case for textContent, so OOB textContent swaps fall through to defaultSwapStyle (innerHTML). The OOB element's HTML gets parsed and inserted as elements rather than literal text.

Click the trigger button above…

After Fix

d53932d4 + fix

Adds case 'textContent' that mirrors the main-swap textContent path: target.textContent = fragment.textContent. Markup inside the OOB element is inserted as literal text.

Click the trigger button above…

The Bug

swapWithStyle() handles every other OOB swap style — none, outerHTML, afterbegin, beforebegin, beforeend, afterend, delete — but has no case for textContent. Falling into the default branch eventually calls swapWithStyle(htmx.config.defaultSwapStyle, ...), which is normally innerHTML. Result: an hx-swap-oob="textContent" response that contains markup gets the markup parsed and inserted as elements instead of being inserted as literal text — the opposite of what the docs describe.

The main-swap path already handles textContent correctly (in the same source file) by setting target.textContent = content directly. The fix mirrors that pattern for OOB swaps.

Both panels send the same mock server response: ok<div id="alerts" hx-swap-oob="textContent"><h1 style="color:red">⚠ Critical Alert</h1></div>. The buggy version renders a red <h1> element. The fixed version renders the literal text "⚠ Critical Alert" — markup and all — exactly as textContent semantics require.

// src/htmx.js — swapWithStyle()
case 'delete':
swapDelete(target)
return
+ case 'textContent':
+ target.textContent = fragment.textContent
+ return
default:
Both panels load htmx from the same base commit (d53932d4), differing only by the 3-line patch shown above. A mock XMLHttpRequest intercepts network calls so htmx runs its full request lifecycle (parse OOB elements → swap → settle) without needing a server.