Swap a DOM Fragment with htmx Without Reloading the Page
Use htmx to fetch an HTML fragment and replace part of a page without a full reload. Includes a worked button example, server headers, and common mistakes.
05 May 2026, 00:28 UTC

Start with the fragment, not the page
Problem: A button that refreshes a list often forces the browser to load an entirely new page. The user loses scroll position and waits for navigation chrome they do not need. Takeaway: htmx can request an HTML fragment from the server and replace only the target element, while the rest of the page stays intact. AJAX here means the browser makes the request in the background without changing the URL.
How the swap works
htmx reads attributes on the triggering element. When the event fires—usually a click—it sends a request to the URL in hx-get. The server should answer with markup only, not a full <html> document. htmx then inserts that markup into the element named by hx-target, using the strategy in hx-swap. The default strategy is innerHTML, which replaces the children of the target.
Worked configuration
Assume a page shows an inbox inside this element:
<div id="inbox">
<!-- existing message rows -->
</div>
A button that refreshes it without reloading the page:
<button hx-get="/messages" hx-target="#inbox" hx-swap="innerHTML">
Refresh inbox
</button>
When clicked, htmx performs a GET to /messages. It expects the response to contain only the markup that belongs inside #inbox, such as message rows. The response replaces the current children of #inbox; the button and surrounding page are not reloaded.
Swap strategies
innerHTML replaces the children of the target. Other strategies include outerHTML, which replaces the target element itself, and beforeend, which appends the fragment inside the target without removing existing children. Choose outerHTML when the target wrapper should be replaced, such as swapping an entire form for a confirmation message.
What the server should return
Return an HTML fragment, not a complete document. For example:
<div class="message">New message from support</div>
<div class="message">Invoice paid</div>
htmx sends HX-Request: true with the request, along with HX-Trigger, HX-Target, and HX-Current-URL. Your route can check HX-Request to decide whether to wrap the fragment in a full page layout or return only the fragment. Authentication and authorization still belong on the server; the fragment endpoint must enforce the same session checks as any other route.
Limits and common mistakes
- Do not use
hx-getfor actions that change state. GET is idempotent and can be triggered by browser prefetchers or accelerators. Usehx-post,hx-put, orhx-deletefor writes. - Do not return JSON or a full HTML document unless you use
hx-selectto narrow the response to the intended element. A full<html><body>payload can insert unexpected markup into the target. - Event listeners do not survive a swap. If htmx replaces an element, any listeners attached directly to removed elements are gone. Use event delegation on a stable ancestor, or re-attach behavior after the swap.
- No JavaScript means no swap. If the htmx script fails to load or is blocked, the button performs no partial update. Critical actions should degrade gracefully or include a server-side fallback.
Verify the behavior
- Open browser developer tools and go to the Network tab.
- Click the button.
- Confirm a GET request to
/messagesappears with the headerHX-Request: true. - Confirm the page URL does not change and the rest of the page does not reload.
- Inspect
#inboxafter the response and confirm its inner HTML matches the fragment returned by the server. - Temporarily block or remove the htmx script tag and click again. The partial update should not occur, confirming the page depends on the client library.
These swap semantics are consistent in recent stable htmx releases. Advanced attributes may differ between versions, so check the release notes if you upgrade.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.