Skip to content
HTMX Swapping & Transitions — Complete Guide to hx-swap, CSS Animations & OOB Swaps

HTMX Swapping & Transitions — Complete Guide to hx-swap, CSS Animations & OOB Swaps

DodaTech Updated Jun 6, 2026 11 min read

Master HTMX swapping strategies to build smooth, animated web applications using innerHTML, outerHTML, morphing, OOB swaps, and CSS transitions.

What You’ll Learn

  • All hx-swap strategies and when to use each one
  • How to animate content swaps with CSS transitions
  • How to use the .htmx-swapping and .htmx-added CSS classes
  • How to update multiple page areas at once with OOB swaps
  • How to preserve element state with hx-preserve
  • How to use the ViewTransitions API for page-level animations

Why Swapping and Transitions Matter

When you update content on a page without a full reload, the change can feel jarring. Content suddenly appears, disappears, or shifts position. Users perceive this as “janky” — it breaks their flow and reduces trust in the application.

Smooth transitions solve this problem. They guide the user’s eye to the changed content, provide feedback that something happened, and make the app feel polished and professional. Security dashboards like Durga Antivirus Pro use animated transitions to draw attention to new threats without disorienting the analyst monitoring the screen.

Your Learning Path

    graph LR
    A[HTMX Getting Started] --> B[Requests & Triggers]
    B --> C[Swapping & Transitions]
    C --> D[Advanced Patterns]
    style C fill:#3b82f6,color:#fff,stroke:#2563eb
    style A fill:#e2e8f0,stroke:#94a3b8
    style B fill:#e2e8f0,stroke:#94a3b8
    style D fill:#e2e8f0,stroke:#94a3b8
  
Prerequisites: You should understand HTMX (hx-get, hx-post, hx-target) from the Getting Started guide. Familiarity with CSS transitions and animations is helpful but not required.

Understanding hx-swap Strategies

The hx-swap attribute controls how the server’s HTML response is inserted into the page. Think of it like a delivery instruction: “put the new content inside the box” vs “replace the entire box” vs “put it next to the box.”

innerHTML (Default)

Replaces the content inside the target element. The target element itself stays in the DOM.

<!-- The div#content stays; only its inner content is replaced -->
<div id="content" hx-get="/api/update" hx-swap="innerHTML">
  Old content here — will be replaced
</div>

When to use: Almost always. It’s the safest and most intuitive strategy.

outerHTML

Replaces the target element itself, including its opening and closing tags. The target element is removed and a new element (from the response) takes its place.

<!-- The entire div#card-1 is removed and replaced by the response -->
<div id="card-1" hx-get="/api/new-card" hx-swap="outerHTML">
  This card replaces itself entirely
</div>

Why use outerHTML? When the container itself needs to change — different CSS class, different tag, or complete replacement. For example, refreshing a widget with a new structure.

Important: After outerHTML, the original element no longer exists. Any event listeners or references to it are lost.

beforebegin / afterbegin / beforeend / afterend

These four strategies insert content relative to the target, rather than replacing its content:

<!-- beforebegin: inserts as a sibling BEFORE the target -->
<div hx-get="/api/item-above" hx-swap="beforebegin">Above</div>

<!-- afterbegin: inserts as the FIRST child of the target -->
<div hx-get="/api/prepend" hx-swap="afterbegin">
  <strong>Parent — new content goes before me</strong>
</div>

<!-- beforeend: inserts as the LAST child of the target -->
<div hx-get="/api/append" hx-swap="beforeend">
  <strong>Parent — new content goes after me</strong>
</div>

<!-- afterend: inserts as a sibling AFTER the target -->
<div hx-get="/api/item-below" hx-swap="afterend">Below</div>

When to use each:

  • afterbegin — prepend to a list or feed
  • beforeend — append to a list or feed (most common)
  • beforebegin / afterend — insert adjacent elements like timeline entries

delete and none

<!-- delete: removes the target element from the DOM -->
<button hx-delete="/api/task/5" hx-swap="delete">
  Delete Task
</button>

<!-- none: fires the request but does NOT change the DOM -->
<button hx-post="/api/analytics" hx-swap="none">
  Record View
</button>

Why use none? For analytics pings, log entries, or background operations where you don’t need a visual update.

Swap Strategies Comparison

StrategyWhat HappensBest Use Case
innerHTMLReplaces content inside targetDefault choice for most operations
outerHTMLReplaces target element itselfRefreshing widgets, changing structure
beforebeginInserts before target (sibling)Adding items above a list
afterbeginInserts as first childPrepending to a list
beforeendInserts as last childAppending to a list
afterendInserts after target (sibling)Adding items below
deleteRemoves target elementDelete operations
noneNo DOM changeAnalytics, logging

CSS Transitions with HTMX

Now that you know how content gets swapped, let’s make it look good.

The .htmx-swapping Class

When HTMX is about to swap content, it adds the htmx-swapping class to the element being removed. This gives you a chance to animate the old content out before the replacement arrives.

<style>
  /* The element starts fully visible */
  .card {
    transition: opacity 0.3s ease, transform 0.3s ease;
  }
  /* When HTMX marks it for swapping, it fades out and slides right */
  .card.htmx-swapping {
    opacity: 0;
    transform: translateX(20px);
  }
</style>

<div class="card" hx-get="/api/next-card" hx-target="this" hx-swap="outerHTML">
  This card fades out before being replaced
</div>

Why this works: HTMX doesn’t remove the element immediately. It first applies the htmx-swapping class and waits for CSS transitions to complete (up to the swap timing you specify). The old element animates out, and only then is it replaced by the new one.

The .htmx-added Class

New content receives the htmx-added class after being inserted. This lets you animate it in:

<style>
  /* New items fade and slide in from above */
  .htmx-added {
    animation: slideIn 0.3s ease-out;
  }
  @keyframes slideIn {
    from {
      opacity: 0;
      transform: translateY(-10px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
</style>

<button hx-get="/api/new-item" hx-target="#list" hx-swap="beforeend">
  Add Item
</button>
<ul id="list">
  <li>Existing item</li>
</ul>

Custom Swap Timing

You can control how long HTMX waits before performing the swap and before “settling” (re-initializing) the new content:

<!-- swap:1s = wait 1 second before swapping (gives old content time to animate out) -->
<div hx-get="/api/data" hx-swap="innerHTML swap:1s">
  Old content animates out over 1 second
</div>

<!-- settle:500ms = wait 500ms after swap for new content to initialize -->
<div hx-get="/api/data" hx-swap="innerHTML settle:500ms">
  New content settles after 500ms
</div>

<!-- Both custom -->
<div hx-get="/api/data" hx-swap="innerHTML swap:300ms settle:500ms">
  Fully custom timing
</div>

Why separate swap and settle? The swap phase is for old content leaving. The settle phase is for new content initializing (like re-binding events or starting animations). Separating them gives you fine-grained control.

ViewTransitions API

The modern View Transitions API provides browser-native animated transitions between page states. HTMX supports it with the transition:true modifier.

<style>
  @keyframes fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
  }
  @keyframes fade-out {
    from { opacity: 1; }
    to { opacity: 0; }
  }
  ::view-transition-old(root) {
    animation: fade-out 0.3s ease;
  }
  ::view-transition-new(root) {
    animation: fade-in 0.3s ease;
  }
</style>

<!-- Enable ViewTransitions for this swap -->
<div hx-get="/api/new-content"
     hx-target="#main"
     hx-swap="innerHTML transition:true">
  Navigate with smooth transition
</div>

Browser support: ViewTransitions API is supported in Chrome 111+, Edge 111+, and Safari 18+. For older browsers, HTMX gracefully falls back to normal swapping.

Morphing Swaps (Idiomorph)

Regular swaps replace the entire target content. Morphing is smarter — it compares the old and new DOM trees and only changes what’s different.

Why morph? Imagine a list of 100 items and one item’s text changes. With innerHTML, the entire list is rebuilt — losing scroll position, input focus, and animation state. With morphing, only the changed text updates.

<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.x.x/dist/ext/morph/morph.js"></script>

<div hx-ext="morph">
  <div hx-get="/api/update" hx-swap="morphdom" hx-target="#list">
    Update List (morph preserves state)
  </div>
</div>

<div id="list">
  <h2>Current Title</h2>
  <p>Only changed elements update.</p>
  <input type="text" value="Preserved value">
</div>

What morphing preserves:

  • Focus and cursor position
  • Input values (unless the server sends different values)
  • Scroll positions
  • Animation state on unchanged elements

Out-of-Band Swaps (hx-swap-oob)

Normally, one request updates one target. OOB (out-of-band) swaps let a single response update multiple elements on the page.

The problem: You submit a comment form. You want to (1) show a success notification, (2) update the comment count, and (3) clear the form. Without OOB, you’d need three separate requests.

The solution: The server includes multiple HTML fragments in one response, each targeting a different page element.

<!-- Server returns this HTML as one response -->
<div id="comments-list">
  <!-- New comment appended here -->
  <div class="comment">Great article!</div>
</div>

<!-- OOB: updates #notification independently -->
<div id="notification" hx-swap-oob="true">
  Comment posted successfully!
</div>

<!-- OOB: updates #comment-count independently -->
<span id="comment-count" hx-swap-oob="innerHTML">
  12 comments
</span>

On the client side, the existing elements are already in the page:

<!-- These already exist on the page -->
<div id="comments-list">...</div>
<div id="notification"></div>
<span id="comment-count">11 comments</span>

HTMX matches elements by id and performs the specified swap on each one.

hx-preserve — Protecting Elements

Some elements should never be replaced — audio players, iframes, or widgets with expensive initialization. hx-preserve protects them:

<div hx-preserve="true" id="music-player">
  <audio controls autoplay>
    <source src="podcast.mp3" type="audio/mpeg">
  </audio>
</div>

Why hx-preserve? Without it, swapping the parent container would destroy the audio player, stopping playback. hx-preserve tells HTMX to keep this element (and its internal state) intact across swaps.

Common Mistakes

1. Forgetting to Animate the Leaving Element

Many developers only add animation for new content (.htmx-added) but forget the old content (.htmx-swapping). Without a leaving animation, content suddenly disappears, which feels abrupt.

2. Setting swap Timing Too Short

If your CSS transition takes 500ms but you set swap:100ms, the transition gets cut off. Always match or exceed your CSS transition duration in the swap timing.

3. Using outerHTML When You Mean innerHTML

outerHTML removes the target element from the DOM. If you later try to reference it by ID, it won’t exist. Use innerHTML unless you specifically need to replace the container.

4. OOB ID Mismatches

OOB swaps require exact id matching between the response HTML and the page. An extra space or different case (userId vs user-id) breaks the swap silently.

5. Not Loading the Morph Extension

hx-swap="morphdom" requires the morph extension script. Without it, HTMX silently falls back to innerHTML. Always include the <script> tag for the extension.

6. Animating on Page Load

Elements with transition properties and hx-trigger="load" may animate in unexpectedly when first rendered. Add prefers-reduced-motion support for accessibility.

Practice Questions

Q1: What’s the difference between innerHTML and outerHTML?

A1: innerHTML replaces content inside the target element (the container stays). outerHTML replaces the target element itself (the container is removed and replaced).

Q2: What CSS class does HTMX add to the element being swapped out?

A2: .htmx-swapping — this class is added to the old element before removal, allowing you to animate its exit.

Q3: How do you update multiple page elements with a single HTMX request?

A3: Use OOB (out-of-band) swaps. Add hx-swap-oob="true" to extra elements in the server response. HTMX matches them by id and swaps them independently.

Q4: What does hx-swap="innerHTML swap:500ms settle:300ms" mean?

A4: Wait 500ms before swapping (giving old content time to animate out), then wait 300ms after swap for the new content to settle (initialize). The swap and settle timings are separate.

Q5: When would you use morphing instead of innerHTML?

A5: When you need to preserve DOM state — input values, focus, scroll position — through updates. Morphing only changes elements that differ, keeping everything else intact.

Challenge: Build a dashboard with a list of widgets that auto-refresh every 10 seconds. Use OOB swaps to update individual widgets independently. Add CSS fade-in/out transitions so widgets animate when content changes. Use hx-preserve to protect a clock widget that should never be replaced.

FAQ

What’s the difference between swap and settle?

Swap is the process of removing old content and inserting new content. Settle is the process after insertion where HTMX re-initializes new elements (applies extensions, processes triggers). Settle timing should account for any “entrance” animations on new elements.

Can I use HTMX transitions with Tailwind CSS?

Yes. Tailwind’s utility classes work perfectly with HTMX transitions. Use transition-all duration-300 for the base transition and define custom styles for .htmx-swapping and .htmx-added in your CSS or Tailwind config.

How do OOB swaps affect performance?

OOB swaps are efficient — HTMX processes them in the same pass as the main swap. Use them instead of making multiple requests. However, avoid OOB-swapping dozens of elements in a single response; batch updates into logical groups.

Does morphing work with form inputs?

Yes. Morphing preserves input values, focus, and cursor position. This makes it ideal for form validation where the server returns the entire form with error messages — the user’s cursor stays where it was.

Try It Yourself

This sandbox demonstrates animated swapping, OOB updates, and morph-style content replacement.

▶ Try It Yourself Edit the code and click Run

What’s Next

TutorialWhat You’ll Learn
HTMX — Advanced PatternsWebSockets, SSE, history management, and production deployment

Related topics: Combine HTMX transitions with Tailwind CSS for utility-first animations, use Alpine.js for client-side interactivity alongside HTMX swaps, and check CSS animation concepts for deeper animation techniques. The swapping patterns you learned here power the animated threat-level dashboards in Durga Antivirus Pro, where smooth transitions help analysts track security events without losing focus.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

What’s Next

Congratulations on completing this Htmx Swapping Transitions tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro