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

  1. Initial Load: Return the page structure immediately (Header, Sidebar, Footer) but fill the “Slow Widgets” with Skeleton Loaders (grey pulsing boxes).
  2. Trigger: The Skeleton HTML includes hx-trigger="load" to immediately ask the server for the real content.
  3. 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, no isLoading state 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.