HTMX Demos
HTMX · 10 Essential Patterns

HTMX — 10 Essential Patterns

Interactive demonstrations of the most useful HTMX UI patterns. Each panel shows a live demo alongside the relevant HTMX markup.

1. Click to Edit

↗ htmx.org/examples/click-to-edit

Inline editing — a view block swaps to a form on demand, then back on save or cancel.

FieldValue
NameAda Lovelace
Emailada@computing.io
RoleMathematician

<!-- View mode --> <div id="contact"> <p>Ada Lovelace</p> <button hx-get="/contact/edit" hx-target="#contact" hx-swap="outerHTML"> Edit </button> </div> <!-- Edit form (server response) --> <form hx-put="/contact" hx-target="#contact" hx-swap="outerHTML"> <input name="name" value="Ada"/> <button type="submit">Save</button> <button hx-get="/contact" hx-target="#contact" hx-swap="outerHTML"> Cancel </button> </form>

2. Bulk Update

↗ htmx.org/examples/bulk-update

Select multiple rows with checkboxes and update their status in a single request.

All Name Status
Alan TuringInactive
Grace HopperActive
John von NeumannInactive
Claude ShannonActive
Linus TorvaldsInactive

<form hx-put="/users/activate" hx-target="#users-table" hx-swap="outerHTML"> <table id="users-table"> <tr> <td> <input type="checkbox" name="ids" value="1"/> </td> <td>Alan Turing</td> <td>Inactive</td> </tr> <!-- more rows ... --> </table> <button type="submit"> Activate Selected </button> </form>

3. Click to Load

↗ htmx.org/examples/click-to-load

Load additional rows on demand — a "Load More" button appends content without reloading.

<tbody id="contacts"> <!-- existing rows --> <tr>...</tr> <!-- trigger row at the bottom --> <tr id="load-more-row"> <td colspan="3"> <button hx-get="/contacts?page=2" hx-target="#load-more-row" hx-swap="outerHTML" hx-indicator="#spinner"> Load More </button> <span id="spinner" class="htmx-indicator"> Loading... </span> </td> </tr> </tbody>

4. Delete Row

↗ htmx.org/examples/delete-row

Issue a DELETE request and swap the target element with an empty string to remove it.

Mercury
Venus
Earth
Mars
Jupiter
Saturn

<tr id="row-mercury"> <td>Mercury</td> <td> <button hx-delete="/planet/mercury" hx-target="closest tr" hx-swap="outerHTML" hx-confirm="Delete Mercury?"> Delete </button> </td> </tr> <!-- Server returns HTTP 200 + --> <!-- empty body; htmx removes --> <!-- the element automatically -->

5. Inline Validation

↗ htmx.org/examples/inline-validation

Validate fields on blur by posting to the server; the response replaces the error element.

<input name="username" hx-post="/validate/username" hx-trigger="blur" hx-target="#username-error" hx-swap="innerHTML"/> <div id="username-error"></div> <!-- Server returns: --> <!-- "" (empty) = valid --> <!-- "<span>Taken</span>" = error --> <!-- Or validate as-you-type: --> <input hx-trigger="keyup changed delay:300ms" .../>

6. Active Search

↗ htmx.org/examples/active-search

Post the search query on every keystroke (debounced) and replace the results container.

<input type="search" name="q" placeholder="Search..." hx-post="/search" hx-trigger="keyup changed delay:300ms" hx-target="#results" hx-indicator="#spinner"/> <span id="spinner" class="htmx-indicator"> Searching... </span> <div id="results"> <!-- server renders matched rows --> </div>

7. Progress Bar

↗ htmx.org/examples/progress-bar

Start a job, then poll the server for status updates to drive a progress indicator.

Idle 0%

Press Start to run the job.

<!-- 1. Start the job --> <button hx-post="/start-job" hx-target="#progress"> Start </button> <!-- 2. Poll for updates --> <div id="progress" hx-get="/job-status" hx-trigger="every 600ms" hx-target="this" hx-swap="outerHTML"> <div style="width:0%"></div> </div> <!-- When done, server omits --> <!-- hx-trigger to stop polling -->

8. Infinite Scroll

↗ htmx.org/examples/infinite-scroll

When the last row becomes visible in the viewport, automatically fetch and append the next page.

Loaded 10 items

<tbody id="contacts"> <!-- normal rows --> <tr>...</tr> <!-- last row triggers on reveal --> <tr hx-get="/contacts?page=2" hx-trigger="revealed" hx-swap="afterend" hx-target="this"> <td>...</td> </tr> </tbody> <!-- hx-trigger="revealed" fires --> <!-- when element enters viewport -->

9. Value Select

↗ htmx.org/examples/value-select

Cascading dropdowns — changing the first select fetches and populates the second from the server.

<select name="category" hx-get="/models" hx-target="#model-select" hx-trigger="change" hx-include="[name='category']"> <option value="llm">Language</option> <option value="img">Image</option> </select> <!-- Populated dynamically --> <select id="model-select"> <!-- server renders <option>s --> </select>

10. Lazy Load

↗ htmx.org/examples/lazy-load

Defer expensive content — the placeholder loads its real content when it scrolls into view.

⏳ Content will load lazily when revealed…

In production this fires automatically via hx-trigger="revealed" when the element scrolls into view.

<!-- Placeholder with lazy trigger --> <div hx-get="/expensive-widget" hx-trigger="revealed" hx-swap="outerHTML"> <!-- shown while waiting --> <div class="htmx-indicator"> Loading... </div> </div> <!-- Server responds with content --> <div class="widget"> ... rendered chart / data ... </div> <!-- Use hx-trigger="load" for --> <!-- immediate deferred loading -->