The Hypermedia Model

An anchor tag is already a hypermedia control: it issues a GET and swaps the response into the whole window. htmx generalizes that one idea - any element, any event, any HTTP verb, any target:

<!-- anchor: click -> GET -> replace the window -->
<a href="/stories">Stories</a>

<!-- htmx: click -> GET -> replace just this div -->
<div hx-get="/stories/page2" hx-target="this">
  Load more
</div>

<!-- any element, any event, any verb -->
<button hx-delete="/stories/42" hx-target="closest article">
  Delete
</button>

The browser does the work. htmx intercepts the event, issues the request with a few HX-* headers, and swaps the response body into the target. No JSON parsing, no client-side template layer, no virtual DOM.

Here is the full request lifecycle for a “load next page” button:

  • The server sees HX-Request: true and can return a fragment instead of a full page.
  • The response body is the new DOM, verbatim. The server decides what the UI becomes; the client just swaps it in.
  • After the swap, htmx settles for the configured 20 ms (default, src/htmx.js:112), giving CSS transitions time to run between the old and new state.

Fragments, not JSON

In a JSON-API app the server ships data and the client decides how to render it. In a hypermedia app the server ships rendered HTML and the decision is already made. This keeps the server as the owner of application state and UI structure - HATEOAS in practice, without the theory.

JSON API response:              htmx response:
{ "stories": [ ... ],          <li class="story">...</li>
  "nextCursor": "abc",         <li class="story">...</li>
  "unreadCount": 3 }           <li class="story">...</li>

client must render all of it    swap it in; done
+ a second request for the badge   <hx-partial id="badge">3</hx-partial>

One response can update several parts of the page using out-of-band swaps or hx-partial envelopes - see Server Integration.

Locality of Behaviour

Behavior lives next to the element it affects. To know what a button does, you read the button:

<button hx-post="/subscribe"
        hx-trigger="click"
        hx-confirm="Subscribe to the weekly digest?">
  Subscribe
</button>

The contrast is a centralized controller that binds handlers to selectors in a separate file. Locality costs you nothing here because there is no build step to fight; the trade-off is that logic is spread across markup, so naming and server-side structure carry more weight. The canonical argument is Carson Gross’s essay in the repo: locality-of-behaviour.md.

Hypermedia-Driven Applications

The term for apps built this way is Hypermedia-Driven Application (HDA): server-rendered HTML, hypermedia exchanges for all mutations, and scripting as an enhancement rather than a requirement. Most HDAs still use some JavaScript - a chart library, a map - but state transitions flow through HTML responses. See hypermedia-driven-applications.md and the practical 10 tips for SSR HDA apps.

Where to next: