# HTMX 4 Integration Guide for Nebula This document details htmx 4 enhancements and patterns optimized for our Go/Templ server-driven architecture. ## Why htmx 4? htmx 4 introduces significant architectural improvements that align perfectly with Go-based server rendering: - **Built-in SSE streaming** - Native support for `text/event-stream` responses - **Morphing swaps** - Preserve DOM state during updates (forms, video, focus) - **Partial tags** - Explicit multi-target updates in single responses - **View Transitions API** - Smooth animated page transitions - **Simplified extensions** - Page-wide, event-based, zero performance penalty - **Enhanced request headers** - Better server-side request detection --- ## Installation ```html ``` **Note:** htmx 4 extensions are bundled in the htmx.org package at `/dist/ext/`. Do NOT use the separate `htmx-ext-*` packages which use the deprecated `defineExtension` API. --- ## Core Patterns for Go/Templ ### 1. Partial Updates with `` The `` tag enables multiple targeted updates in a single response - perfect for Go handlers that need to update several page sections. **Go Handler:** ```go func handleAddToCart(w http.ResponseWriter, r *http.Request) { // Return multiple partials in one response components.CartPartials(cartData).Render(r.Context(), w) } ``` **Templ Component:** ```templ templ CartPartials(data CartData) { { strconv.Itoa(data.Count) } @CartItem(data.NewItem) { data.FormattedTotal } } ``` **HTML Trigger:** ```html ``` ### 2. Morphing Swaps (Preserve State) Use `innerMorph` or `outerMorph` to update content while preserving: - Form input values - Focus state - Video/audio playback - Scroll position **Use Cases:** ```html
``` **Morph Control Attributes:** ```html
Never touch this
Ephemeral content
``` ### 3. View Transitions Enable smooth animated transitions between page states. **Global Enable:** ```javascript htmx.config.transitions = true; ``` **Per-Element:** ```html ``` **CSS Customization:** ```css ::view-transition-group(*) { animation-duration: 150ms; } ::view-transition-old(root) { animation: fade-out 150ms ease-out; } ::view-transition-new(root) { animation: fade-in 150ms ease-in; } ``` ### 4. Streaming Responses (SSE) htmx 4 has native SSE support - no extension needed. **Go Handler:** ```go func handleProgress(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") flusher := w.(http.Flusher) for progress := 0; progress <= 100; progress += 10 { // Send HTML fragment fmt.Fprintf(w, "data:
\n\n", progress) flusher.Flush() time.Sleep(500 * time.Millisecond) } // Final update fmt.Fprintf(w, "data:
Done!
\n\n") } ``` **HTML:** ```html
``` **Custom Events in SSE:** ```go // Server sends custom event fmt.Fprintf(w, "event: notification\n") fmt.Fprintf(w, "data: {\"count\": 5}\n\n") ``` ```html
``` --- ## Request Detection in Go ### htmx 4 Request Headers | Header | Description | Example Value | |--------|-------------|---------------| | `HX-Request` | Always "true" for htmx requests | `true` | | `HX-Request-Type` | "partial" or "full" | `partial` | | `HX-Target` | Target element selector | `#results` | | `HX-Trigger` | Triggering element ID | `search-input` | | `HX-Boosted` | Request from boosted element | `true` | | `HX-Current-URL` | Current page URL | `/dashboard` | **Go Middleware/Helper:** ```go func IsHTMXRequest(r *http.Request) bool { return r.Header.Get("HX-Request") == "true" } func IsPartialRequest(r *http.Request) bool { return r.Header.Get("HX-Request-Type") == "partial" } func GetHTMXTarget(r *http.Request) string { return r.Header.Get("HX-Target") } ``` **Pattern: Full Page vs Partial Response:** ```go func handleDashboard(w http.ResponseWriter, r *http.Request) { data := getDashboardData() if IsHTMXRequest(r) && IsPartialRequest(r) { // Return only the content fragment views.DashboardContent(data).Render(r.Context(), w) return } // Return full page with layout views.DashboardPage(data).Render(r.Context(), w) } ``` --- ## Response Headers from Go Control client behavior via response headers: ```go func handleAction(w http.ResponseWriter, r *http.Request) { // Redirect client-side (no full page reload) w.Header().Set("HX-Location", "/new-page") // Push URL to browser history w.Header().Set("HX-Push-Url", "/updated-url") // Replace current URL (no history entry) w.Header().Set("HX-Replace-Url", "/current") // Trigger client-side events w.Header().Set("HX-Trigger", "itemAdded") w.Header().Set("HX-Trigger-After-Swap", "refreshCart") // Override swap behavior w.Header().Set("HX-Reswap", "outerHTML") w.Header().Set("HX-Retarget", "#different-target") // Force full page refresh w.Header().Set("HX-Refresh", "true") } ``` --- ## Attribute Inheritance htmx 4 uses explicit inheritance with the `:inherited` modifier. **Pattern: Shared Confirmation:** ```html
``` **Pattern: Shared Target:** ```html
``` **Enable Implicit Inheritance (htmx 2 behavior):** ```javascript htmx.config.implicitInheritance = true; ``` --- ## Advanced Triggers ### Trigger Modifiers ```html
``` --- ## Loading Indicators ```html
``` **CSS for indicators:** ```css .htmx-indicator { opacity: 0; transition: opacity 200ms ease-in; } .htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; } ``` --- ## Boosting & Navigation Convert standard links/forms to AJAX: ```html Navigate with History
Smooth Navigate
``` --- ## Preload Extension Preload content on hover/mousedown for instant navigation: ```html Fast Link Preloaded Link ``` --- ## Confirmation Dialogs ```html ``` --- ## Best Practices for Go/Templ ### 1. Use Partials for Multi-Target Updates Instead of multiple requests or complex OOB swaps, return `` tags. ### 2. Leverage Morphing for Stateful Components Use `innerMorph`/`outerMorph` for forms, media players, and interactive widgets. ### 3. Detect Request Type Server-Side Return minimal HTML fragments for partial requests, full pages for direct navigation. ### 4. Use Response Headers for Side Effects Trigger client events, update URLs, and control swapping via headers. ### 5. Preload Critical Paths Add `preload="mousedown"` to navigation links for perceived instant loading. ### 6. Stream Long Operations Use SSE (`text/event-stream`) for progress indicators and real-time updates. --- ## Configuration Reference ```javascript // Enable all htmx 4 features htmx.config.transitions = true; // View Transitions API htmx.config.implicitInheritance = false; // Explicit :inherited (default) htmx.config.selfRequestsOnly = true; // Security: same-origin only htmx.config.timeout = 0; // Request timeout (0 = none) htmx.config.historyCacheSize = 10; // History cache entries ``` **Via Meta Tag:** ```html ```