Skip to content

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.

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+

<section id="summary" hx-swap-oob="true">
    Updated summary
</section>

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+

<hx-partial hx-target="#notifications" hx-swap="append">
<!-- Add content here. -->
</hx-partial>

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+

{% partialdef partial_name %}
<!-- Add content here. -->
{% endpartialdef %}

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+

{% partialdef partial_name inline %}
<!-- Add content here. -->
{% endpartialdef %}

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+

{% partial partial_name %}

Endpoint or context: The referenced partial must be defined in the same template or resolved through Django's supported partial syntax.