--- name: htmx-patterns description: HTMX patterns for Django including partial templates, hx-* attributes, and dynamic UI without JavaScript. Use when building interactive UI, handling AJAX requests, or creating dynamic components. --- # HTMX Patterns for Django ## Core Philosophy - Server renders HTML, not JSON - HTMX requests return HTML fragments, not data - Partial templates for dynamic updates - separate `_partial.html` files for HTMX responses - Progressive enhancement - pages work without JavaScript, HTMX enhances UX - Minimal client-side complexity - let the server do the heavy lifting ## Critical Hints & Reminders ### UX Best Practices **Always include loading indicators** - Use `hx-indicator` to show loading states during requests - Users should never wonder if their action worked - Example: `` **Always provide user feedback** - Use Django messages framework for success/error feedback - Return error messages in HTMX responses, not silent failures - Show what happened after an action completes **Handle errors gracefully** - Return proper HTTP status codes (400 for validation errors, 500 for server errors) - Render form errors in partial templates - Don't swallow exceptions - log and show user-friendly messages ### Django-Specific Patterns **Always detect HTMX requests** - Check `request.headers.get("HX-Request")` to detect HTMX requests - Return partial templates for HTMX, full page templates otherwise - Pattern: `if request.headers.get("HX-Request"): return render(request, "_partial.html", context)` **Always return partials for HTMX** - HTMX requests should return `_partial.html` templates, not full pages with `base.html` - Full page responses to HTMX requests break the UX and send duplicate HTML - Partials should be self-contained HTML fragments **Always validate request.method** - Check `request.method == "POST"` before processing form data - Return proper status codes (405 Method Not Allowed for wrong methods) **CSRF is already configured globally** - The base template has `hx-headers` on `` - no need to add CSRF tokens to individual forms - All HTMX requests automatically include the CSRF token ### Template Organization **Naming convention** - Partials: `_partial.html` (underscore prefix) - Full pages: `page.html` (no prefix) - Example: `posts/list.html` (full page) includes `posts/_list.html` (partial) **Structure** - Full page template extends `base.html` and includes partial - Partial contains only the dynamic HTML fragment - HTMX targets the partial's container div **Keep partials focused** - Each partial should represent one logical UI component - Avoid partials that are too large or do too much - Compose larger UIs from multiple smaller partials ## Django View Patterns ### HTMX Detection Check the `HX-Request` header to detect HTMX requests: ```python def my_view(request): context = {...} if request.headers.get("HX-Request"): return render(request, "app/_partial.html", context) return render(request, "app/full_page.html", context) ``` ### Form Handling Pattern Key points: - Validate form normally - On success: return partial with new data OR trigger client-side event - On error: return partial with form errors - Always handle both HTMX and non-HTMX cases ```python def create_view(request): if request.method == "POST": form = MyForm(request.POST) if form.is_valid(): obj = form.save() if request.headers.get("HX-Request"): return render(request, "app/_item.html", {"item": obj}) return redirect("app:list") # Return form with errors if request.headers.get("HX-Request"): return render(request, "app/_form.html", {"form": form}) else: form = MyForm() return render(request, "app/create.html", {"form": form}) ``` ## Response Headers Reference HTMX respects special response headers for client-side behavior: ### HX-Trigger Trigger client-side events after response - Use case: Update other parts of page after action - Example: `response["HX-Trigger"] = "itemCreated"` - Template listens: `
` ### HX-Redirect Client-side redirect - Use case: Redirect after successful action - Example: `response["HX-Redirect"] = reverse("app:detail", args=[obj.pk])` ### HX-Retarget / HX-Reswap Override hx-target and hx-swap from server - Use case: Different targets for success vs error - Success: `response["HX-Retarget"] = "#main"` - Error: Return partial without changing target (targets the form) ### HX-Refresh Force full page refresh - Use case: Major state change that affects whole page - Example: `response["HX-Refresh"] = "true"` ## Common Pitfalls - **Missing loading indicators**: Always use `hx-indicator` - users click multiple times without feedback - **Full pages in HTMX responses**: Return `_partial.html`, not full pages with `base.html` - check `HX-Request` header - **Not handling form errors**: Always return the form with errors on validation failure, not just the success case - **Not disabling buttons**: Use `hx-disabled-elt="this"` to prevent duplicate submissions - **N+1 queries**: HTMX views need `select_related()`/`prefetch_related()` just like regular views ## Integration with Other Skills - **django-templates**: Partial template organization and inheritance patterns - **django-forms**: HTMX form submission and validation - **django-extensions**: Use `show_urls` to verify HTMX endpoints - **pytest-django-patterns**: Testing HTMX endpoints and headers - **systematic-debugging**: Debug HTMX request/response issues