Skeleton Loading & HTMX
Skeleton Loading with Go & HTMX
Users hate waiting. If your dashboard takes 2 seconds to calculate “Total Revenue,” the entire page shouldn’t hang for 2 seconds.
In standard MPAs (Multi-Page Apps), the browser waits for the full HTML response. In the GOTH stack, we use Lazy Loading with Skeleton screens.
The Pattern
- Initial Load: Return the page structure immediately (Header, Sidebar, Footer) but fill the “Slow Widgets” with Skeleton Loaders (grey pulsing boxes).
- Trigger: The Skeleton HTML includes
hx-trigger="load"to immediately ask the server for the real content. - Swap: The server calculates the expensive data and returns the real HTML, swapping out the skeleton.
Implementation
1. The Dashboard Handler (Fast)
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
// Render the layout immediately.
// Notice we don't fetch revenue here.
Layout(
// Inject Skeletons
components.RevenueSkeleton(),
components.UsersSkeleton(),
).Render(r.Context(), w)
}2. The Skeleton Component (Templ)
templ RevenueSkeleton() {
<div hx-get="/api/revenue"
hx-trigger="load"
hx-swap="outerHTML"
class="animate-pulse bg-gray-200 h-32 rounded">
<!-- Pulsing Grey Box -->
</div>
}3. The Real Data Handler (Slow)
func RevenueHandler(w http.ResponseWriter, r *http.Request) {
// Extensive DB calculation...
time.Sleep(1 * time.Second)
amount := db.CalculateRevenue()
// Return real component
components.RevenueCard(amount).Render(r.Context(), w)
}Why this wins
- TTFB (Time To First Byte): 10-20ms. The user sees the UI instantly.
- Perceived Performance: The app feels alive while data loads in parallel.
- Simplicity: No
useEffect, noisLoadingstate management, no JSON parsing. Just HTML swapping.
Advanced: Request Coalescing
If 10 widgets all fire hx-get at once, you might hit browser connection limits (HTTP/1.1 limit is 6). * HTTP/2 or HTTP/3: Go’s net/http supports these automatically if you use TLS. They multiplex requests, so 10 requests cost nearly the same connection overhead as 1. * Ensure your server supports TLS (Let’s Encrypt) to enable H2/H3.