Snippets and Examples¶
The extension contributes secure Django-first HTMX patterns for django-html documents.
Type a prefix to insert the snippet, then move through its editable placeholders.
Mutating forms include Django's CSRF token. The examples use HTMX syntax shared by the supported HTMX 2 and HTMX 4 catalogs. See Core Django response contracts for matching view and response examples.
Prefixes¶
| Prefix | Classification | Description |
|---|---|---|
htmx-autosave |
Curated recipe | CSRF-safe debounced Django autosave form |
htmx-toggle |
Curated recipe | CSRF-safe server-authoritative boolean toggle |
htmx-soft-delete-undo |
Curated recipe | CSRF-safe soft delete that returns a server-authorized undo state |
htmx-field-check |
Curated recipe | Debounced side-effect-free Django field availability check |
htmx-autocomplete |
Curated recipe | Debounced Django autocomplete using a native datalist |
htmx-get |
Common | Django URL-backed HTMX GET button |
htmx-post |
Common | CSRF-safe Django form submitted through HTMX |
htmx-delete |
Common | CSRF-safe Django delete form submitted through POST |
htmx-search |
Common | Debounced Django HTMX search input |
htmx-pagination |
Common | Django pagination with fragment replacement and browser history |
htmx-load-more |
Common | Explicit Django load-more link with a replaceable sentinel |
htmx-filter-sort |
Curated recipe | URL-backed Django filter and sort form with HTMX updates |
htmx-form-validation |
Common | Django form replaced with server-rendered validation state |
htmx-file-upload |
Curated recipe | CSRF-safe multipart file upload with a status target |
htmx-bulk-actions |
Curated recipe | CSRF-safe bulk action form for Django objects |
htmx-dependent-dropdown |
Curated recipe | Django select that loads options for a dependent field |
htmx-status-form |
Curated recipe | HTMX 4 Django form with status-specific validation handling |
htmx-infinite |
Common | Load the next Django page when revealed |
htmx-poll |
Curated recipe | Poll a Django view until work is complete |
htmx-lazy |
Common | Load a Django fragment when its placeholder appears |
htmx-boost-nav |
Curated recipe | Progressively enhance Django navigation with history updates |
htmx-progress |
Curated recipe | Poll server-rendered progress for a Django task |
htmx-click-to-edit |
Common | Replace a Django object summary with an edit form |
htmx-table-row |
Curated recipe | Replace a Django table row with server-rendered editing controls |
htmx-dialog |
Curated recipe | Load a script-free non-modal dialog from a Django view |
htmx-modal |
Curated recipe | Load a Django view into a fixed CSS-only modal overlay |
htmx-tabs |
Curated recipe | Replace server-rendered tabs and selected state |
htmx-morph |
Curated recipe | HTMX 4 state-preserving refresh |
htmx-oob-swap |
Common | Update a second region from a Django HTMX response |
htmx-toast |
Curated recipe | Append an accessible notification from an HTMX response |
htmx-partial-response |
Curated recipe | HTMX 4 response fragment with explicit target and swap |
partialdef |
Django 6 | Define a Django 6 template partial |
partialdef-inline |
Django 6 | Define and render an inline Django 6 template partial |
partial |
Django 6 | Render a same-file Django 6 template partial |
Requests and forms¶
htmx-autosave¶
CSRF-safe debounced Django autosave form.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'autosave-view' object.pk %}"
hx-post="{% url 'autosave-view' object.pk %}"
hx-trigger="input changed delay:750ms"
hx-sync="this:replace"
hx-target="#save-status"
hx-swap="outerHTML">
{% csrf_token %}
<!-- Add content here. -->
<button type="submit">Save draft</button>
<output id="save-status" aria-live="polite">Not saved yet</output>
</form>
Endpoint or context: The POST view validates and saves the draft, then returns the replacement save-status element.
Request kind: POST
Context variables: form, object
Security notes: Keep the CSRF token and enforce authorization in the view.
Accessibility notes: Use the live output for save state announcements.
Response contract: Return a server-confirmed live save-status element.
Related snippets: htmx-form-validation, htmx-search
htmx-toggle¶
CSRF-safe server-authoritative boolean toggle.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<article id="item-{{ object.pk }}">
<!-- Add content here. -->
<form method="post"
action="{% url 'toggle-view' object.pk %}"
hx-post="{% url 'toggle-view' object.pk %}"
hx-target="closest article"
hx-swap="outerHTML">
{% csrf_token %}
<button type="submit" aria-pressed="{{ object.enabled|yesno:'true,false' }}">Toggle</button>
</form>
</article>
Endpoint or context: The POST view changes the server state and returns the complete article with its new state.
Request kind: POST
Context variables: object
Security notes: Authorize the state change in the Django view.
Accessibility notes: Keep aria-pressed synchronized with server state.
Response contract: Return the same article root with the canonical boolean state.
Related snippets: htmx-soft-delete-undo
htmx-soft-delete-undo¶
CSRF-safe soft delete that returns a server-authorized undo state.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<article id="item-{{ object.pk }}">
{% if object.deleted %}
<p role="status">Deleted</p>
<form method="post"
action="{% url 'undo-view' object.pk %}"
hx-post="{% url 'undo-view' object.pk %}"
hx-target="closest article"
hx-swap="outerHTML">
{% csrf_token %}
<button type="submit">Undo</button>
</form>
{% else %}
<!-- Add content here. -->
<form method="post"
action="{% url 'delete-view' object.pk %}"
hx-post="{% url 'delete-view' object.pk %}"
hx-confirm="Delete this item?"
hx-target="closest article"
hx-swap="outerHTML">
{% csrf_token %}
<button type="submit">Delete</button>
</form>
{% endif %}
</article>
Endpoint or context: The delete view marks the object deleted and the undo view reverses that server state; both return the article root.
Request kind: POST
Context variables: object
Security notes: Use authorization and a server-side undo policy.
Accessibility notes: Expose the deleted state through a status message.
Response contract: Return the canonical article or undo tombstone after each state transition.
Related snippets: htmx-delete, htmx-toggle
htmx-field-check¶
Debounced side-effect-free Django field availability check.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<input id="field"
name="value"
hx-get="{% url 'check-view' %}"
hx-trigger="input changed delay:400ms"
hx-sync="this:replace"
hx-target="#field-status"
hx-swap="outerHTML">
<span id="field-status" aria-live="polite"></span>
Endpoint or context: The GET view checks the value without changing state and returns a replacement field-status element.
Request kind: GET
Context variables: field value
Accessibility notes: Announce availability through the live status element.
Response contract: Return a bounded status fragment and keep the endpoint side-effect-free.
Related snippets: htmx-form-validation, htmx-autocomplete
htmx-autocomplete¶
Debounced Django autocomplete using a native datalist.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<input id="search"
type="search"
name="q"
list="suggestions"
hx-get="{% url 'autocomplete-view' %}"
hx-trigger="input changed delay:250ms"
hx-sync="this:replace"
hx-target="#suggestions"
hx-swap="innerHTML">
<datalist id="suggestions"></datalist>
Endpoint or context: The autocomplete view returns bounded option elements for the native datalist.
Request kind: GET
Context variables: query parameter
Accessibility notes: Retain the native input and datalist fallback.
Response contract: Return bounded option elements only.
Related snippets: htmx-search, htmx-field-check
htmx-get¶
Django URL-backed HTMX GET button.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<button type="button"
hx-get="{% url 'view-name' %}"
hx-target="#content"
hx-swap="innerHTML">Load</button>
Endpoint or context: The view returns the HTML fragment that should replace the selected target.
htmx-post¶
CSRF-safe Django form submitted through HTMX.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'view-name' %}"
hx-post="{% url 'view-name' %}"
hx-target="#content"
hx-swap="innerHTML">
{% csrf_token %}
<!-- Add content here. -->
</form>
Endpoint or context: The POST view validates the submitted fields and returns the replacement fragment for the selected target.
htmx-delete¶
CSRF-safe Django delete form submitted through POST.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'delete-view' object.pk %}"
hx-post="{% url 'delete-view' object.pk %}"
hx-confirm="Are you sure?"
hx-target="closest .item"
hx-swap="outerHTML">
{% csrf_token %}
<button type="submit">Delete</button>
</form>
Endpoint or context: The POST view performs the deletion and returns either an empty response or replacement markup for the item.
htmx-search¶
Debounced Django HTMX search input.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<input type="search"
name="q"
hx-get="{% url 'search-view' %}"
hx-trigger="input changed delay:300ms, search"
hx-sync="this:replace"
hx-target="#results">
Endpoint or context: The search view reads the input value and returns the current result-list fragment.
Request kind: GET
Context variables: query parameter
Accessibility notes: Keep an accessible results-region label.
Response contract: Return the current results fragment.
Related snippets: htmx-filter-sort
htmx-pagination¶
Django pagination with fragment replacement and browser history.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<section id="results">
<!-- Add content here. -->
<nav aria-label="Pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}"
hx-get="?page={{ page_obj.previous_page_number }}"
hx-target="#results"
hx-swap="outerHTML"
hx-push-url="true">Previous</a>
{% endif %}
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}"
hx-get="?page={{ page_obj.next_page_number }}"
hx-target="#results"
hx-swap="outerHTML"
hx-push-url="true">Next</a>
{% endif %}
</nav>
</section>
Endpoint or context: The list view returns a full page for normal navigation and the complete results section for an HTMX request.
Request kind: GET
Context variables: page_obj
Accessibility notes: Keep previous and next links as native navigation fallbacks.
Response contract: Return the stable results root and vary page and fragment responses on HX-Request.
Related snippets: htmx-filter-sort, htmx-load-more
htmx-load-more¶
Explicit Django load-more link with a replaceable sentinel.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
{% if page_obj.has_next %}
<li class="load-more">
<a href="?page={{ page_obj.next_page_number }}"
hx-get="?page={{ page_obj.next_page_number }}"
hx-target="closest li"
hx-swap="outerHTML">Load more</a>
</li>
{% endif %}
Endpoint or context: The endpoint returns the next list items followed by a new load-more sentinel when another page exists.
Request kind: GET
Context variables: page_obj
Accessibility notes: The link remains keyboard-accessible without HTMX.
Response contract: Return list items and the next sentinel, not a full page.
Related snippets: htmx-pagination, htmx-infinite
htmx-filter-sort¶
URL-backed Django filter and sort form with HTMX updates.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<form method="get"
action="{% url 'list-view' %}"
hx-get="{% url 'list-view' %}"
hx-include="this"
hx-trigger="change, input changed delay:300ms"
hx-sync="this:replace"
hx-target="#results"
hx-swap="outerHTML"
hx-push-url="true">
<input type="search" name="q" value="{{ request.GET.q }}">
<!-- Add content here. -->
<button type="submit">Apply</button>
</form>
Endpoint or context: The list view returns a full page for ordinary GET navigation and the complete results root for an HTMX request.
Request kind: GET
Context variables: request.GET
Accessibility notes: The submit button provides a non-JavaScript fallback.
Response contract: Return the stable results root and preserve canonical query parameters in the URL.
Related snippets: htmx-search, htmx-pagination
htmx-form-validation¶
Django form replaced with server-rendered validation state.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'validate-view' %}"
hx-post="{% url 'validate-view' %}"
hx-target="this"
hx-swap="outerHTML">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save</button>
</form>
Endpoint or context: The view returns the complete bound form with HTTP 200 when invalid, or a complete success fragment when valid.
htmx-file-upload¶
CSRF-safe multipart file upload with a status target.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'upload-view' %}"
enctype="multipart/form-data"
hx-post="{% url 'upload-view' %}"
hx-encoding="multipart/form-data"
hx-target="#upload-result"
hx-swap="innerHTML"
hx-indicator="#upload-indicator">
{% csrf_token %}
<label for="upload-file">Choose a file</label>
<input id="upload-file" type="file" name="file" required>
<button type="submit">Upload</button>
<span id="upload-indicator" class="htmx-indicator" role="status">Uploading…</span>
</form>
<div id="upload-result" aria-live="polite"></div>
Endpoint or context: The upload view reads request.FILES, validates the file, and returns success or error markup for the upload-result target.
htmx-bulk-actions¶
CSRF-safe bulk action form for Django objects.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'bulk-view' %}"
hx-post="{% url 'bulk-view' %}"
hx-target="#items-body"
hx-swap="outerHTML">
{% csrf_token %}
<table>
<thead>
<tr>
<th scope="col">Select</th>
<th scope="col">Item</th>
</tr>
</thead>
<tbody id="items-body">
{% for object in objects %}
<tr>
<td>
<input type="checkbox"
name="selected"
value="{{ object.pk }}"
aria-label="Select {{ object }}">
</td>
<td>{{ object }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Apply to selected</button>
</form>
Endpoint or context: The POST view processes every selected value and returns a complete tbody element with the same id.
htmx-dependent-dropdown¶
Django select that loads options for a dependent field.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<label for="parent-select">Category</label>
<select id="parent-select"
name="category"
hx-get="{% url 'options-view' %}"
hx-trigger="change"
hx-target="#dependent-select"
hx-swap="innerHTML"><!-- Add content here. --></select>
<label for="dependent-select">Option</label>
<select id="dependent-select" name="option"></select>
Endpoint or context: The options view reads the selected parent value and returns option elements for the dependent select.
htmx-status-form¶
HTMX 4 Django form with status-specific validation handling.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<form method="post"
action="{% url 'view-name' %}"
hx-post="{% url 'view-name' %}"
hx-target="#result"
hx-status:422="swap:innerHTML target:#errors"
hx-status:5xx="swap:none">
{% csrf_token %}
<div id="errors" role="alert"></div>
<!-- Add content here. -->
<button type="submit">Save</button>
</form>
<div id="result"></div>
Endpoint or context: Requires HTMX 4. Return validation HTML with status 422, and success HTML for the result container. Server error responses do not swap.
Loading and navigation¶
htmx-infinite¶
Load the next Django page when revealed.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
{% if page_obj.has_next %}
<div hx-get="?page={{ page_obj.next_page_number }}"
hx-trigger="revealed"
hx-target="this"
hx-swap="outerHTML"
role="status">Loading…</div>
{% endif %}
Endpoint or context: The list view returns the next result fragment followed by a new next-page trigger when another page exists.
htmx-poll¶
Poll a Django view until work is complete.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<div hx-get="{% url 'status-view' task.pk %}"
hx-trigger="every 5s"
hx-target="this"
hx-swap="outerHTML"
role="status"
aria-live="polite">Waiting…</div>
Endpoint or context: The status view returns the same polling region while work continues and omits hx-trigger after completion.
htmx-lazy¶
Load a Django fragment when its placeholder appears.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<section hx-get="{% url 'fragment-view' %}"
hx-trigger="load"
hx-target="this"
hx-swap="outerHTML"
aria-busy="true">
<p class="htmx-indicator" role="status">Loading…</p>
</section>
Endpoint or context: The fragment view returns the complete replacement section without aria-busy.
htmx-boost-nav¶
Progressively enhance Django navigation with history updates.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<nav aria-label="Primary">
<a href="{% url 'home' %}"
hx-boost="true"
hx-target="#main-content"
hx-select="#main-content"
hx-swap="outerHTML"
hx-push-url="true">Home</a>
<!-- Add content here. -->
</nav>
<main id="main-content" tabindex="-1">
</main>
Endpoint or context: Each linked view returns a normal full page containing the same main-content element, which hx-select extracts.
htmx-progress¶
Poll server-rendered progress for a Django task.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<div hx-get="{% url 'progress-view' task.pk %}"
hx-trigger="every 1s"
hx-target="this"
hx-swap="outerHTML"
role="status"
aria-live="polite">
<label for="task-progress">Progress</label>
<progress id="task-progress" value="{{ task.progress }}" max="100">{{ task.progress }}%</progress>
</div>
Endpoint or context: The progress view returns updated markup and omits hx-trigger when the task reaches a terminal state.
Editing and UI¶
htmx-click-to-edit¶
Replace a Django object summary with an edit form.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
<article>
<h2>{{ object }}</h2>
<button type="button"
hx-target="closest article"
hx-swap="outerHTML"
hx-get="{% url 'edit-view' object.pk %}">Edit</button>
</article>
Endpoint or context: The edit view returns a CSRF-protected form that targets and replaces itself after submission.
htmx-table-row¶
Replace a Django table row with server-rendered editing controls.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<tr id="item-{{ object.pk }}">
<th scope="row">{{ object }}</th>
<td>
<button type="button"
hx-target="closest tr"
hx-swap="outerHTML"
hx-get="{% url 'edit-view' object.pk %}">Edit</button>
</td>
</tr>
Endpoint or context: The edit view returns a complete replacement row containing a CSRF-protected update form.
htmx-dialog¶
Load a script-free non-modal dialog from a Django view.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<button type="button"
hx-get="{% url 'dialog-view' object.pk %}"
hx-target="#dialog"
hx-swap="outerHTML"
aria-controls="dialog">Open dialog</button>
<dialog id="dialog" aria-labelledby="dialog-title"></dialog>
Endpoint or context: The view returns a dialog with the same id, the open attribute, a labelled title, and a native method="dialog" close form; it is non-modal without showModal().
htmx-modal¶
Load a Django view into a fixed CSS-only modal overlay.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<button type="button"
hx-get="{% url 'modal-view' %}"
hx-target="#modal"
hx-swap="innerHTML"
aria-haspopup="dialog"
aria-controls="modal">Open modal</button>
<div id="modal"
class="modal-container"
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"></div>
Endpoint or context: The modal view returns the overlay markup with underlay/close controls that target the same container and swap it empty to close; no JavaScript is required.
htmx-tabs¶
Replace server-rendered tabs and selected state.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<section id="tabs" aria-label="Sections">
<nav role="tablist">
<a id="tabs-overview"
role="tab"
aria-selected="true"
aria-controls="tabs-panel"
href="{% url 'tabs-view' 'overview' %}"
hx-get="{% url 'tabs-view' 'overview' %}"
hx-target="#tabs"
hx-swap="outerHTML"
hx-push-url="true">Overview</a>
<!-- Add content here. -->
</nav>
<div id="tabs-panel"
role="tabpanel"
aria-labelledby="tabs-overview">{{ tab_content }}</div>
</section>
Endpoint or context: The tabs view returns the complete section with updated tablist, aria-selected state, and panel content.
htmx-morph¶
HTMX 4 state-preserving refresh.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<section id="live-region"
hx-get="{% url 'fragment-view' %}"
hx-trigger="every 5s"
hx-swap="innerMorph">
<!-- Add content here. -->
</section>
Endpoint or context: Requires HTMX 4. The view returns the region contents. Use stable element IDs to help morphing preserve focus and state.
Server responses¶
htmx-oob-swap¶
Update a second region from a Django HTMX response.
Classification: Common
HTMX versions: 2, 4
Django versions: 4.2+
Endpoint or context: Return this fragment alongside the primary response; HTMX replaces the existing element with the matching id.
htmx-toast¶
Append an accessible notification from an HTMX response.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
<div id="notifications" hx-swap-oob="beforeend:#notifications">
<p role="status" aria-live="polite">Saved successfully.</p>
</div>
Endpoint or context: Return this fragment with a primary response to append its status message to the existing notifications region.
htmx-partial-response¶
HTMX 4 response fragment with explicit target and swap.
Classification: Curated recipe
HTMX versions: 2, 4
Django versions: 4.2+
Endpoint or context: Requires HTMX 4. Return one or more hx-partial elements to update multiple targets. This is an HTMX response element, separate from Django partialdef template tags.
Django partials¶
partialdef¶
Define a Django 6 template partial.
Classification: Django 6
HTMX versions: 2, 4
Django versions: 4.2+
Endpoint or context: Django 6 registers the named block as a reusable partial in the current template.
partialdef-inline¶
Define and render an inline Django 6 template partial.
Classification: Django 6
HTMX versions: 2, 4
Django versions: 4.2+
Endpoint or context: Django 6 registers and renders the named partial at its definition site.
partial¶
Render a same-file Django 6 template partial.
Classification: Django 6
HTMX versions: 2, 4
Django versions: 4.2+
Endpoint or context: The referenced partial must be defined in the same template or resolved through Django's supported partial syntax.