Migration
Where to look when an upgrade breaks something, and what to do about it. ghtmx is pre-1.0, so breaking changes to language syntax, generated-code shape, and the runtime API may land between minor versions โ always with a migration note.
There are two independent axes, and they move for different reasons:
| Axis | What changes it | Where it is written down |
|---|---|---|
| The ghtmx version you build with | go get, go install, the install script |
CHANGELOG.md โ every Changed and Removed entry carries a Migration: note |
| The htmx version you pin | htmxVersion in ghtmx.json |
ghtmx.dev/docs/htmx-versions |
Bumping the module does not change your htmx pin, and changing the pin does not require a module bump. Read both when you do both.
Upgrading ghtmx itself
- Read
CHANGELOG.mdfrom your current version forward. Every entry under### Changedor### Removedcarries aMigration:line โ that is enforced by a build gate, so the absence of one means the entry is genuinely drop-in. - Run
ghtmx generateand rebuild. Generated files carry the version that produced them (// ghtmx: version: vX.Y.Z), soghtmx generate -checkin CI reports drift the moment the committed output and the pinned toolchain disagree. - Keep the toolchain and the module in step.
ghtmx versionprints what the binary is; therequire github.com/go-monolith/ghtmxline ingo.modis what your code links against.ghtmx generatewarns when they differ.
A binary built from a source checkout reports vX.Y.Z-dev and stamps
that into anything it generates. That is a development build, not a
release โ regenerate with an installed release before committing
generated output.
Moving from htmx 2 to htmx 4
The full path is ghtmx.dev/docs/htmx-versions, which lists every difference the compiler can see and maps each htmx 2 construct to the diagnostic you will get and the htmx 4 replacement. The short version:
- Set
"htmxVersion": "4.0.0"inghtmx.jsonand runghtmx generate. Removed and renamed constructs reportGHTMX-E0501naming their replacement, so the compiler drives the edit list. - Fix the
GHTMX-W0202warnings: htmx 4 inheritance is explicit, so an inheritable attribute on a wrapper needs:inherited. The CSRF header is the one that bites โhx-headers={ ghtmx.CSRFHeader(token) }on a layout silently stops reaching the elements that issue requests, and every unsafe request is rejected. Writehx-headers:inherited={ ... }. - Replace
Emit<Event>AfterSwapandEmit<Event>AfterSettlewith the plainEmit<Event>: htmx 4 removed theHX-Trigger-After-SwapandHX-Trigger-After-Settleresponse headers they set. Listen onhtmx:after:swapwhere the timing mattered. - Check the handler side, which the compiler cannot see:
HX-SourcereplacesHX-Triggeron requests,hx-deleteno longer sends form values on its own, and 4xx/5xx responses swap by default. - Pinning back to
2.0.10reverses every check, so the two generations cannot mix unnoticed.
The three htmx4-* examples in examples are working applications on
the new surface.
Upgrading to v0.2.0: the CSRF safe-method list
Called out separately because it is the one change in v0.2.0 that reaches applications with no htmx 4 in them.
auth.SafeMethod โ and so the CSRF middleware of every adapter โ
exempts QUERY alongside GET, HEAD, and OPTIONS from v0.2.0 onward.
QUERY is safe and idempotent by specification and is what htmx 4's
hx-query issues. The middleware is runtime code and cannot see the
compile-time htmxVersion pin, so the widened list applies whether or
not the project pins htmx 4 โ bumping the module is enough.
It only matters if some route answers QUERY, which is easier to hit
than it sounds: an http.ServeMux pattern that names no method matches
every method, so a plain mux.Handle("/x", h) reaches its handler on
QUERY, and Fiber v3 carries QUERY in its DefaultMethods, so an
app.All(...) route or a handler mounted as middleware does too. gin
and echo route QUERY only where a route names it explicitly.
-
If a handler mutates state on QUERY, move that work to POST, PUT, PATCH, or DELETE. It must not mutate on a safe method regardless of what the middleware does.
-
To keep the pre-v0.2.0 behaviour instead, give the middleware its own safe-list:
htmx2 := auth.WithSafeMethods( http.MethodGet, http.MethodHead, http.MethodOptions) auth.CSRF(htmx2) // net/http and chi fiberv3auth.CSRF(htmx2) // and identically for gin, echo, fiber
AUTH.md documents the whole CSRF layer, including the two token
channels and why the token stays alongside SameSite.
Where the notes live
CHANGELOG.mdโ per-versionMigration:notes. It is assembled fromchangelog.d/fragments at release time, never edited by hand.AUTH.mdโ sessions, CSRF, and the safe-method list.CONFIG.mdโ everyghtmx.jsonkey and CLI flag, includinghtmxVersion.DIAGNOSTICS.mdโ every diagnostic code, including theGHTMX-E0501andGHTMX-W0202families the htmx 4 move relies on.- ghtmx.dev/docs/htmx-versions โ the htmx 2 versus 4 comparison and the step-by-step move.