Skip to content

HTMX Swapping — Complete Guide with Examples

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you'll learn about HTMX swapping strategies. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

HTMX swapping strategies define how server responses replace the target element in the DOM, from basic content replacement to complex CSS-powered transition animations.

What You'll Learn

By the end of this tutorial, you'll use all eight swap strategies, apply swap animations with CSS transitions, use morphing for smooth DOM reconciliation, implement out-of-band swaps, and control swap timing.

Why It Matters

The swap Strategy determines how content transitions on your page. A well-chosen swap makes updates feel natural: appending items to a list, replacing the entire card, or removing elements with animation.

Real-World Use

DodaZIP uses different swap strategies for different actions: innerHTML for content updates, outerHTML for replacing entire blocks, beforeend for appending log entries, and delete for removing completed items with animation.

Where This Fits in Your Learning Path

flowchart LR
    A["HTMX Triggers"] --> B["**HTMX Swapping**"]
    B --> C["Synchronization"]
    C --> D["History & Indicators"]
    D --> E["Advanced HTMX"]
    style B fill:#3b82f6,stroke:#2563eb,color:#fff
    style A fill:#e2e8f0,stroke:#94a3b8
    style E fill:#e2e8f0,stroke:#94a3b8

The Eight Swap Strategies

<!-- innerHTML (default): replaces content inside the target -->
<div hx-get="/api/content" hx-swap="innerHTML">
  This content is replaced
</div>

<!-- outerHTML: replaces the target element itself -->
<div id="card-1" hx-get="/api/new-card" hx-swap="outerHTML">
  This entire div is replaced
</div>

<!-- beforebegin: inserts before the target -->
<div hx-get="/api/before" hx-swap="beforebegin">
  Response appears before this div
</div>

<!-- afterbegin: inserts as first child -->
<div hx-get="/api/prepend" hx-swap="afterbegin" hx-target="#list">
  <ul id="list"><li>Existing item</li></ul>
</div>

<!-- beforeend: inserts as last child (appending) -->
<button hx-get="/api/add-item" hx-swap="beforeend" hx-target="#list">
  <ul id="list"><li>Existing item</li></ul>
</button>

<!-- afterend: inserts after the target -->
<div hx-get="/api/after" hx-swap="afterend">
  Response appears after this div
</div>

<!-- delete: removes the target element -->
<button hx-delete="/api/items/1" hx-swap="delete">
  Delete this item's parent
</button>

<!-- none: process the request without DOM changes -->
<button hx-post="/api/log-visit" hx-swap="none">
  Log silently
</button>

Expected output: Each strategy places the response differently relative to the target.

Swap with CSS Transitions

Apply CSS transitions when swapping content.

<div hx-get="/api/smooth-update"
     hx-target="#content"
     hx-swap="innerHTML transition:true">
  <div id="content">Content fades during updates</div>
</div>

<style>
  .htmx-swapping {
    opacity: 0;
    transition: opacity 0.3s ease-out;
  }
</style>

Expected output: The target content fades out, then the new content fades in during the swap.

Morphing Swaps

Use hx-swap="morph" for smooth DOM reconciliation that preserves element state.

<div hx-get="/api/list-update"
     hx-target="#list"
     hx-swap="morph">

  <ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
    <!-- When the server returns a reordered
         or partially updated list, morphing
         preserves unmodified elements -->
  </ul>
</div>

Expected output: Only changed list items are updated. Unchanged items keep their DOM state (including event listeners and focused state).

Swap Timing Control

Control swap timing with settle and swap delays.

<button hx-get="/api/delayed-swap"
        hx-target="#content"
        hx-swap="innerHTML swap:500ms settle:300ms">
  Content appears after 500ms delay
</button>

<!-- With transition -->
<div hx-get="/api/animated"
     hx-target="#box"
     hx-swap="innerHTML transition:true swap:300ms settle:200ms">
  <div id="box"></div>
</div>

Expected output: swap:500ms delays the content swap by 500ms. settle:300ms waits 300ms before processing new content.

Out-of-Band Swaps (OOB)

Update multiple elements from a single response using hx-swap-oob.

<!-- Server response includes OOB markers -->
<!-- <div id="sidebar" hx-swap-oob="true">
  Updated sidebar content
</div>
<div id="main">
  Updated main content
</div> -->

<button hx-get="/api/dashboard-update"
        hx-target="#main">
  Refresh Dashboard
</button>
<div id="main">Main content</div>
<div id="sidebar">Sidebar (updated via OOB)</div>

Expected output: The response updates both #main (via normal target) and #sidebar (via OOB swap) in a single request.

Common Mistakes

1. Using outerHTML for the target with hx-target

If you use outerHTML, the target element is replaced. Any future requests targeting that element's ID will fail because the element no longer exists.

2. Forgetting that innerHTML removes event listeners

innerHTML replaces all content inside the target, removing any event listeners or Alpine.js components inside.

3. Using beforeend when you need afterbegin

beforeend appends as the last child. afterbegin prepends as the first child. Choose based on where the new content should appear.

4. Not handling settle timing for complex responses

Complex HTML with nested alpine components needs settle time. Increase settle delay for complex content.

5. Using delete on the wrong element

delete removes the element itself. If hx-target points to a parent, the entire parent is removed.

Practice Questions

  1. What is the default swap strategy? innerHTML, which replaces the content inside the target element.

  2. What does hx-swap="delete" do? It removes the target element from the DOM without inserting the response.

  3. What is the difference between beforebegin and afterbegin? beforebegin inserts before the target element (as a sibling). afterbegin inserts as the first child of the target.

  4. How do you update multiple elements from one response? Use hx-swap-oob="true" on elements in the response that should update other targets.

  5. What does the transition:true modifier do? It enables CSS transition animations during the swap, fading out old content and fading in new content.

Challenge

Build a todo list where adding a new item uses beforeend, deleting uses delete with a CSS fade-out animation, and editing uses outerHTML to replace the entire row.

FAQ

Can I use custom swap strategies?

Yes. HTMX extension API allows custom swap strategies via the htmx.defineExtension function.

What happens during the settle phase?

HTMX processes new content, applying attributes and loading resources. The settle phase completes before new interactions are allowed.

{{< faq "Does hx-swap="none" make any changes?" "No. The request is made and the response is ignored for DOM changes. Useful for logging, analytics, or server-side side effects." >}}

Can I swap content from an external source?

HTMX handles same-origin responses. For cross-origin, the server must include appropriate CORS headers.

How does morphing preserve input values?

Morphing compares old and new DOM trees. Elements that exist in both keep their current state. Only new or changed elements are updated.


Mini Project

Build a chat interface that uses multiple swap strategies. New messages append with beforeend. Editing a message replaces it with outerHTML. Deleting uses delete with a CSS transition. The chat input uses swap:none to clear silently.

<div id="chat-messages" hx-get="/api/chat/messages" hx-trigger="every 5s" hx-swap="innerHTML">
  <!-- Messages load here -->
</div>

<form hx-post="/api/chat/send"
      hx-target="#chat-messages"
      hx-swap="beforeend"
      hx-on::after-request="this.reset()">
  <input name="message" required placeholder="Type a message...">
  <button type="submit">Send</button>
</form>

<!-- Each message (returned from server): -->
<!-- <div class="message" id="msg-123">
  <span class="text">Hello!</span>
  <button hx-get="/api/chat/123/edit"
          hx-target="closest .message"
          hx-swap="outerHTML">Edit</button>
  <button hx-delete="/api/chat/123"
          hx-target="closest .message"
          hx-swap="delete swap:300ms">Delete</button>
</div> -->

What's Next

Learn about request synchronization:

Tutorial What You'll Learn
HTMX Synchronization Coordinate multiple HTMX requests and avoid race conditions
HTMX History Manage browser history and navigation

Related topics: CSS transitions and animations, DOM insertion techniques.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro