hx-push-url: Browser History API Synchronization & Back/Forward Cache
- Last updated
- Prerequisites:
- HTML5 History API (`pushState`, `popstate`)
- HTMX hx-get
- htmx
- hx-push-url
- history-api
- pushState
- navigation
- url
Read at your depth
The practical view
`hx-push-url="true"` (or passing a specific path `hx-push-url="/contacts/1"`) updates the browser's address bar using `history.pushState()` when an HTMX request succeeds. HTMX snapshots the current DOM state into `localStorage` before swapping, so clicking the browser's Back button restores the previous view instantly without re-requesting the server.
The same idea in other frameworks
React Router `useNavigate()` pushing to `history` stack.Angular Router `router.navigate()` with browser location sync.Vue Router `router.push()`.`<a hx-get="/contacts" hx-push-url="true" hx-target="#content">Contacts</a>`.Legacy vs modern
Un-synced AJAX Partial vs Deep-Linkable hx-push-url
Without `hx-push-url`, the browser address bar stays frozen; with it, URLs update and back/forward navigation works.
<button hx-get="/tab2" hx-target="#main">Tab 2</button> // ❌ URL remains /tab1, back button breaks<button hx-get="/tabs/settings" hx-push-url="true" hx-target="#main"> // ✅ NEW: Full URL syncInterview gotchas
Context
Interviewers test your knowledge of HTMX server response control headers.
The mechanical answer
The server can send the **`HX-Push-Url: /new-url`** response header. This overrides client-side attributes and instructs HTMX to push `/new-url` onto the browser's history stack immediately upon swapping.
Trap
Sending `HX-Push-Url: false` explicitly prevents a URL from being pushed even if the client element had `hx-push-url="true"`.
Context
Checks understanding of the dual-purpose endpoint pattern.
The mechanical answer
If a user navigates via HTMX (which sends `HX-Request: true`), the server returns a partial (`<div>Settings</div>`). If `hx-push-url="/tabs/settings"` updated the address bar, and the user hits **F5 (hard refresh)**, the browser sends a standard request *without* `HX-Request: true`. If the server blindly returns only the partial, the user sees broken unstyled HTML. The server must check for `HX-Request` and return the *full page shell* on direct requests.
Trap
Always wrap backend handlers in middleware that serves full layouts when `HX-Request` is absent.