Reactivity patterns
HTMX covers every DX pattern React developers reach for, with a tiny fraction of the toolchain. Each pattern below has a one-page walkthrough with the markup and the server-side handler shape. A runnable demo of each lives in the starter at /patterns/.
If you're coming from TanStack Query, the mental swap is: your server action already is the cache invalidation. Return the fresh partial, htmx swaps it in place, done. No client cache to mutate.
#The 15 patterns
- Debounced live search —
hx-trigger="input changed delay:400ms". - Server-side sort — sortable column headers,
hx-getwithhx-push-url. - Paginated list — prev/next + numbered pages, one swap per click.
- Infinite scroll —
hx-trigger="intersect once"with cursor pagination. - Inline validation (422 target) — return 422 and htmx re-renders the form partial.
- Modal form with validation — dialog + form scoped errors, close on success via
HX-Trigger. - Optimistic delete —
hx-swap="outerHTML swap:0s"removes the row immediately. - Toast after action (out-of-band swap) — server appends to refresh toasts everywhere.
- Counter sync — badges and totals stay fresh via OOB swaps.
- Multi-target update — one request refreshes two regions via
hx-swap-oob.- Tabs with lazy content —
hx-trigger="click once"loads each tab's body on first reveal.- Async autocomplete (combobox) —
hx-trigger="keyup changed delay:300ms".- Polling for progress —
hx-trigger="every 2s"until done.- Cascading selects — select-on-change re-renders the next select.
- Real-time "is typing" (SSE companion) —
hx-ext="sse"for long-lived signal channels.#Runtime requirements
All patterns work with the starter's default stack:
html <!-- HTMX (server roundtrips) --> <script defer src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script> <script defer src="https://unpkg.com/htmx-ext-alpine-morph@2.0.0/alpine-morph.js"></script> <!-- Alpine (client state) --> <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/morph@3.14.1/dist/cdn.min.js"></script> <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.14.1/dist/cdn.min.js"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>The
PageOrPartialhelper inInfrastructure/Web/AjaxExtensions.csreturns a full page for normal GETs and a partial for requests carryingHX-Request: true— so every pattern's handler is one method that works both ways.#Two layers, clean separation
Concern Tool Example Server roundtrips, swaps, partials HTMX hx-get,hx-post,hx-target,hx-swap,hx-trigger,hx-swap-oobClient-only state Alpine x-data,x-show,x-on:click,x-cloak,x-persistComponent markup Noundry.UI tags ,,Style Tailwind v4 via Tailbreeze utility classes, no Node toolchain The
htmx-ext-alpine-morphextension preserves Alpine state across HTMX swaps so checkboxes, expanded panels, and modal-open flags stay correct after a partial re-render.#When to reach for what
- Pure server data → HTMX. Forms, lists, sorts, paginations, validation. Server stays the source of truth.
- Pure client state → Alpine. Modal open/close, dropdown toggles, theme persistence, "show more" expansions.
- Mixed → both, in that order. Wrap the HTMX trigger in if you need a local "is submitting" flag during the round-trip.