This content originally appeared on DEV Community and was authored by sisproid
Hey there! I’ve been wrestling with HTMX loading strategies for a while now, and I figured it’s time to share what I’ve learned. This isn’t some groundbreaking revelation – just practical stuff that’s helped me ship better apps.
You know that moment when you’re staring at a component and thinking, “Should I render this server-side or let HTMX grab it later?” Yeah, I’ve been there too. A lot.
After building a handful of HTMX apps (and making plenty of mistakes along the way), I’ve started to see some patterns that actually make this decision pretty straightforward. Let me walk you through my thought process.
The “Oh Crap, Everything is Loading” Moment
Picture this: You’ve just deployed your shiny new dashboard, and your user opens it to find… a bunch of loading spinners. Everything is fetching data via HTMX because, hey, it’s cool and modern, right?
Wrong. I learned this the hard way.
The user sees a skeleton page for what feels like forever, and you realize you’ve prioritized “clean architecture” over actual user experience. Been there, done that, got the angry Slack messages to prove it.
The Simple Question That Changed Everything
Here’s what I ask myself now, and it’s surprisingly effective:
“Does the user need this to understand what page they’re on?”
If yes → server-side rendering. If no → HTMX can handle it.
That’s it. Most of the time, anyway.
Real Examples from Real Projects
Let me show you some actual scenarios I’ve dealt with:
The E-commerce Product Page
What I used to do:
<!-- Everything loaded via HTMX -->
<div hx-get="/api/product/123" hx-trigger="load">Loading product...</div>
<div hx-get="/api/reviews/123" hx-trigger="load delay:300ms">Loading reviews...</div>
<div hx-get="/api/recommendations/123" hx-trigger="load delay:600ms">Loading recommendations...</div>
Result: User stares at loading spinners, probably thinks the site is broken, potentially bounces.
What I do now:
<!-- Critical stuff rendered server-side -->
<div class="product-info">
<h1>{{ product.title }}</h1>
<div class="price">${{ product.price }}</div>
<button class="buy-now">Add to Cart</button>
</div>
<!-- Nice-to-have stuff loaded later -->
<div hx-get="/api/reviews/{{ product.id }}"
hx-trigger="load delay:500ms">
<div class="reviews-placeholder">Loading reviews...</div>
</div>
Result: User sees the product immediately, can make a purchase decision, and reviews show up as a bonus.
The Dashboard That Taught Me Patience
I once built a dashboard where everything was an API call. The main stats, the charts, the notifications – everything. It was “architecturally pure” but felt slow as molasses.
The fix was embarrassingly simple: render the quick stuff server-side, load the heavy stuff progressively.
<!-- Fast queries (< 100ms) rendered immediately -->
<div class="quick-stats">
<span>Total Orders: {{ stats.totalOrders }}</span>
<span>Revenue: ${{ stats.revenue }}</span>
</div>
<!-- Expensive analytics loaded later -->
<div hx-get="/api/analytics/complex-report"
hx-trigger="load delay:800ms">
<div class="chart-skeleton">Crunching numbers...</div>
</div>
My Personal “Rules of Thumb”
After plenty of trial and error, here’s what guides my decisions:
Render Server-Side When:
- The query is fast (I use 100ms as my cutoff)
- Users need it immediately (product title, user name, etc.)
- SEO matters (anything Google should see)
- It’s the main reason someone visited the page
Use HTMX Loading When:
- The query takes forever (> 200ms in my experience)
- It’s “nice to have” content (comments, recommendations)
- It’s personalized (hard to cache effectively)
- It’s below the fold (users might not even scroll down)
The Numbers That Actually Matter
Okay, this might sound a bit technical, but stick with me. I’ve found these thresholds really helpful:
- Under 100ms query time: Just include it server-side
- 100-200ms: Judgment call (usually server-side unless there’s a good reason)
- Over 200ms: Definitely HTMX territory
Why these numbers? They’re not scientific – they’re just what I’ve observed works well in practice. Under 100ms, users don’t notice the wait. Over 200ms, they start to feel it.
The Loading State That Actually Helps
Here’s something I learned: if you’re going to load something via HTMX, make the loading state useful.
Don’t do this:
<div hx-get="/api/comments" hx-trigger="load delay:500ms">
Loading...
</div>
Do this:
<div hx-get="/api/comments" hx-trigger="load delay:500ms">
<div class="comments-skeleton">
<h4>Comments ({{ commentCount }})</h4>
<div class="skeleton-comment"></div>
<div class="skeleton-comment"></div>
</div>
</div>
The skeleton gives users a sense of what’s coming and makes the wait feel shorter.
When I Mix Both Approaches
Sometimes the best solution is a hybrid. Here’s a pattern I use a lot:
<!-- Critical content server-rendered -->
<main class="dashboard">
<h1>Welcome back, {{ user.name }}!</h1>
<!-- Quick stats included -->
<div class="overview">
<span>Unread messages: {{ quickStats.messages }}</span>
<span>Tasks due: {{ quickStats.tasks }}</span>
</div>
<!-- Heavy stuff loaded progressively -->
<section hx-get="/api/detailed-analytics"
hx-trigger="load delay:400ms">
<div class="analytics-skeleton">Loading detailed analytics...</div>
</section>
</main>
This way, users get immediate value but also get enhanced functionality as things load.
The Mistakes I Still Make
Let me be honest – I still mess this up sometimes:
- Loading everything client-side because it feels “cleaner”
- Not staggering load delays and overwhelming the server
- Forgetting about error states (what if the HTMX call fails?)
- Making loading states too generic instead of giving users context
A Simple Decision Framework
When I’m unsure, I ask myself these questions in order:
- Is this critical for the user’s first impression? → Server-side
- Will this query take more than 200ms? → HTMX
- Do I need this for SEO? → Server-side
- Is this personalized data that’s hard to cache? → HTMX
- When in doubt? → Start with server-side, optimize later
The Real-World Test
Here’s my favorite validation: ask a non-technical friend to use your app. If they ask “Is it still loading?” about something that should be immediately visible, you probably need to render it server-side.
What I Wish Someone Had Told Me Earlier
Start simple. Render the important stuff server-side first. Get that working well. Then, identify the slow or secondary features and progressively enhance them with HTMX.
Don’t try to architect the perfect loading strategy from day one. Build it, measure it, improve it.
Performance matters, but perceived performance matters more. A page that shows 80% of its content immediately feels faster than one that shows 100% after a delay.
My Current Go-To Pattern
These days, most of my pages follow this structure:
<!-- Server-rendered critical path -->
<main>
<!-- Hero/primary content here -->
{{ include 'critical-content' }}
<!-- Secondary content loaded progressively -->
<section hx-get="/api/secondary-data"
hx-trigger="load delay:300ms">
<!-- Meaningful loading state -->
</section>
<aside hx-get="/api/sidebar-widgets"
hx-trigger="load delay:600ms">
<!-- Another meaningful loading state -->
</aside>
</main>
It’s not revolutionary, but it works consistently well.
Wrapping Up
Look, there’s no perfect formula here. Every app is different, every user base has different expectations. But I’ve found that starting with “user needs this immediately” vs. “user can wait a bit for this” gets you 90% of the way there.
The remaining 10% is just experience and iteration. Build it, ship it, measure it, improve it.
And remember – your users don’t care about your architecture. They care about getting their stuff done quickly and without friction. Keep that in mind, and you’ll make the right decisions more often than not.
What’s your experience been with HTMX loading strategies? I’d love to hear about the patterns you’ve discovered in your own projects. Drop a comment and let’s learn from each other!
This content originally appeared on DEV Community and was authored by sisproid