htmx-patterns
SKILL.md
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.htmlfiles 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-indicatorto show loading states during requests - Users should never wonder if their action worked
- Example:
<button hx-get="/data/" hx-indicator="#spinner">Load</button>
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.htmltemplates, not full pages withbase.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-headerson<body>- 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) includesposts/_list.html(partial)
Structure
- Full page template extends
base.htmland 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:
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
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:
<div hx-get="/count/" hx-trigger="itemCreated from:body">
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 withbase.html- checkHX-Requestheader - 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_urlsto verify HTMX endpoints - pytest-django-patterns: Testing HTMX endpoints and headers
- systematic-debugging: Debug HTMX request/response issues
Weekly Installs
11
Repository
kjnez/claude-code-djangoGitHub Stars
84
First Seen
Jan 23, 2026
Security Audits
Installed on
opencode10
gemini-cli10
codex10
github-copilot9
cursor9
claude-code8