Back

Smart Loading Patterns with htmx

Smart Loading Patterns with htmx

You’ve built a dashboard. Every component fires hx-get on load. The user sees six spinning loaders while waiting for content that could have arrived with the initial page. Sound familiar?

The problem isn’t htmx—it’s treating every piece of content the same way. Smart UI loading patterns require understanding when to load content, not just how. This article covers the htmx loading patterns that actually matter: viewport-triggered loading, staged delays, and progressive enhancement through hx-boost.

Key Takeaways

  • Ask whether content must exist before users can act—if yes, render it server-side; if no, defer it with htmx
  • Use hx-trigger="load" with staggered delays to prevent request storms and create visual cascades
  • Choose revealed for standard page scrolling and intersect for scroll containers or threshold control
  • Apply hx-boost for instant navigation without changing server code or breaking progressive enhancement

Why Loading Strategy Matters

htmx keeps state on the server. That’s its strength. But network requests still cost time, and users notice when critical content arrives late.

The core question: Does this content need to exist before the user can act?

If yes, render it server-side. If no, htmx can fetch it later. This simple filter eliminates most loading pattern confusion.

Lazy Loading with htmx: The load Trigger

The hx-trigger="load" pattern fires a request when an element enters the DOM. It’s useful for deferring expensive queries while keeping the initial page fast.

<section hx-get="/api/analytics" 
         hx-trigger="load delay:300ms"
         hx-swap="innerHTML">
    <div class="skeleton">Loading analytics...</div>
</section>

The delay modifier prevents request storms. Stagger your delays—300ms for medium-priority content, 600ms or more for low-priority sections. This spreads server load and creates a natural visual cascade.

When to use load:

  • Content below the fold
  • Personalized data that’s hard to cache
  • Queries exceeding 200ms

When to avoid it:

  • Primary page content users expect immediately
  • SEO-critical information
  • Fast queries under 100ms (just render them server-side)

Viewport-Triggered Loading: revealed vs intersect

htmx provides two triggers for viewport-based loading. Choosing the right one depends on your layout.

The revealed Trigger

revealed fires once when an element scrolls into view. It uses a simple visibility check against the document viewport.

<div hx-get="/api/recommendations"
     hx-trigger="revealed"
     hx-swap="outerHTML">
    Loading recommendations...
</div>

This works well for standard page scrolling—infinite scroll implementations, lazy-loaded images, or content sections users might never reach.

The intersect Trigger

intersect uses the Intersection Observer API and accepts a threshold modifier. Use it when you need finer control or when content lives inside a scrollable container.

<div hx-get="/api/items"
     hx-trigger="intersect once threshold:0.5"
     hx-swap="innerHTML">
    <div class="placeholder"></div>
</div>

The once modifier prevents repeated requests. The threshold:0.5 means the element must be 50% visible before triggering.

Choose intersect when:

  • Loading within scroll containers (not the main viewport)
  • You need threshold control
  • You want explicit Intersection Observer behavior

Choose revealed when:

  • Standard document scrolling
  • Simpler semantics are sufficient

Progressive Enhancement with hx-boost

hx-boost converts standard links and forms into AJAX-powered requests without changing your server code. The browser swaps the <body> content while minimizing <head> reprocessing.

<body hx-boost="true">
    <a href="/contacts">Contacts</a>
    <a href="/settings">Settings</a>
    <a href="/report.pdf" hx-boost="false">Download Report</a>
</body>

This is progressive loading at its simplest. Navigation feels faster because scripts and styles don’t reload. History and back-button behavior work normally. If JavaScript fails, links still function as standard navigation.

Override with hx-boost="false" for file downloads or external links that shouldn’t be intercepted.

Controlling Request Races with hx-sync

Multiple triggers can create race conditions. hx-sync coordinates requests on related elements.

<input hx-get="/search"
       hx-trigger="keyup changed delay:200ms"
       hx-sync="closest form:replace">

The replace strategy cancels in-flight requests when a new one starts. Other strategies include queue and drop. Use this when rapid user input could fire overlapping requests.

Preserving Already-Loaded Content

When htmx swaps content, it replaces the target by default. Use hx-preserve on elements that shouldn’t reload—video players, form inputs with user data, or components with internal state.

<video id="player" hx-preserve="true">...</video>

The element persists across swaps as long as its id matches.

The Decision Framework

Before adding hx-trigger="load" to anything, ask:

  1. Is this critical for understanding the page? → Server-side render
  2. Does the query take over 200ms? → Lazy load
  3. Is this below the fold? → Use revealed or intersect
  4. Is this personalized? → Lazy load (caching won’t help)

Start with server-rendered content. Add htmx performance patterns only where they solve real problems—slow queries, personalized data, or content users might not need.

Conclusion

The best loading pattern is often no loading pattern at all. Render what matters, defer what doesn’t, and let the server stay in control. By applying the decision framework—server-render critical content, lazy load slow queries, and use viewport triggers for below-the-fold sections—you avoid the spinning loader trap and deliver interfaces that feel immediate.

FAQs

The revealed trigger uses a simple visibility check against the document viewport and fires once when an element scrolls into view. The intersect trigger uses the Intersection Observer API, giving you threshold control and proper behavior inside scrollable containers. Use revealed for standard page scrolling and intersect when you need finer control.

No. Lazy loading adds network round trips that can make critical content feel slow. Only defer content that users don't need immediately, takes over 200ms to query, sits below the fold, or contains personalized data that can't be cached. Fast queries under 100ms should render server-side.

Use hx-sync to coordinate requests on related elements. The replace strategy cancels in-flight requests when a new one starts. You can also use queue to process requests in order or drop to ignore new requests while one is pending. This is especially useful for search inputs with keyup triggers.

No. When hx-boost intercepts navigation, htmx properly updates the browser history. The back and forward buttons work as expected. If JavaScript fails entirely, boosted links fall back to standard navigation since they're regular anchor tags with valid href attributes.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay