From e7896b3e313cc17382e6d6073753708e776b1acf Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Wed, 7 Jan 2026 12:56:00 -0500 Subject: [PATCH] docs(htmx): add htmx 4 integration guide for Nebula --- HTMX.md | 570 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 HTMX.md diff --git a/HTMX.md b/HTMX.md new file mode 100644 index 0000000..10b3cc2 --- /dev/null +++ b/HTMX.md @@ -0,0 +1,570 @@ +# 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 + +```