Use templ with htmx

Two ways to do partial updates: the generic hx-* pattern (works with any server) and templ’s native fragment rendering.

Pattern 1: hx-* attributes

htmx posts a request and swaps in whatever the server returns. The trick: give the element an id and use hx-select so htmx extracts the matching piece from the full page the server sends back.

<form id="countsForm"
      hx-post="/"
      hx-select="#countsForm"
      hx-swap="outerHTML">
    <!-- inputs -->
</form>

The server just renders the full page - it does not need to know htmx exists. htmx receives the whole document and keeps only the selected element. Inline handlers via hx-on:* attributes are supported in templ markup too.

This pattern is simple but sends more bytes than necessary. Native fragments fix that.

Pattern 2: native fragments

Mark a subtree with templ.Fragment, then tell the handler which fragments to serve:

templ countsForm(n int) {
    @templ.Fragment("countsForm") {
        <form id="countsForm">
            <output>{ fmt.Sprint(n) }</output>
        </form>
    }
}

templ page(n int) {
    @layout(countsForm(n))
}
mux.Handle("POST /counts", templ.Handler(page(n), templ.WithFragments("countsForm")))

templ.WithFragments (handler.go) makes the templ.Fragment markers addressable (fragment.go). Requests carrying the fragment id get just that subtree.

What happens on a fragment request:

  • The full page renders into a buffer first; only the fragment is emitted
  • Because output is buffered, an error mid-render produces no partial HTML on the wire
  • Non-fragment requests fall through to the normal response

Gotcha: A fragment request returns the whole page if the server forgot templ.WithFragments. Both halves must agree: the handler option and the fragment id in the request.

Which to use

Start with fragments when you control both ends - fewer bytes, explicit boundaries. Use hx-select when the same endpoint also serves non-htmx clients and you do not want a second code path.

For the client-side half (attributes, swaps, triggers, events), see the lab’s htmx topic.