History, errors, and enhancement

Push a URL into history

Give a substantial content change a shareable location and make back and forward navigation restore it.

Fragment swaps update the page without changing the address bar. That is fine for small edits. When an interaction creates a navigable state, such as a filter, a tab, or a selected resource, push a URL with hx-push-url.

<a href="/tasks?status=complete"
  hx-get="/tasks?status=complete"
  hx-target="#task-view"
  hx-push-url="true">
  Completed tasks
</a>

After the swap, the address bar shows /tasks?status=complete. DevTools shows GET /tasks?status=complete with a response body that is the #task-view fragment: a list of completed task rows. HTMX snapshots the current page state before navigation so the Back button can restore it. If the snapshot is unavailable, HTMX requests the URL with HX-History-Restore-Request: true.

Every pushed URL must work when pasted into a new tab or refreshed. That route should return a complete document for ordinary navigation and enough page HTML for history restoration, not only the inner list fragment. A realistic bug: you push /tasks?status=complete but a direct GET returns 404 because the server only serves fragments to HTMX. Fix: the same route must render a full page when HX-Request is absent.

You can set hx-push-url to an explicit URL when the request route and public location differ. Keep that mapping rare and obvious. Otherwise the Network request, browser location, and server routing become hard to reason about.

Do not push transient states such as “menu open” or “save indicator visible.” Push search, filters, pagination, or selected resources that people may share and revisit.

Test direct load, refresh, Back, and Forward, not only the forward click. If Back restores a stale task count or an open modal from the snapshot, your restore response or snapshot boundaries need work.

Boosted navigation and explicit hx-push-url can coexist. Boost pushes link URLs automatically. Fragment swaps need you to opt in. Do not push twice for the same navigation.

Lesson completed