htmx-patterns
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
More from kjnez/claude-code-django
pytest-django-patterns
pytest-django testing patterns, Factory Boy, fixtures, and TDD workflow. Use when writing tests, creating test factories, or following TDD red-green-refactor cycle.
24django-forms
Django form handling patterns including ModelForm, validation, clean methods, and HTMX form submission. Use when building forms, implementing validation, or handling form submission.
16celery-patterns
Celery task patterns including task definition, retry strategies, periodic tasks, and best practices. Use when implementing background tasks, scheduled jobs, or async processing.
15onboard
Onboard Claude to a new task by exploring the codebase, building context, and preparing to implement. Use when starting a new task, feature, or bug fix that requires understanding the codebase first. Triggers on requests like "onboard me", "get ready for this task", "explore and prepare", "/onboard".
4pr-review
Review a pull request using project standards. Use when the user wants to review a PR, check code quality, or get structured feedback on changes. Accepts a PR number or URL as argument. Triggers on requests like "review PR 123", "check this pull request", "/pr-review 123".
4worktree-commit-merge
Commit worktree changes, merge into master/main, then sync the worktree branch with master. Trigger when the user says things like "commit and merge to master", "we're done with this worktree, commit and merge", or any similar phrasing. Don't wait for the user to spell out "worktree" — if there are changes and they mention merging to master/main, use this skill.
4