How htmx Handles HTTP Responses Based on Status Codes
htmx uses the HTTP status code of every XHR or fetch response to determine whether to execute a content swap, trigger an error event, or process a special redirect, with the core logic implemented in src/htmx.js.
The bigskysoftware/htmx library treats HTTP status codes as the primary control mechanism for DOM updates and error handling. When a request completes, htmx inspects xhr.status to route the response through either a success pipeline that executes content swaps or an error pipeline that triggers cleanup events. This approach allows developers to declaratively handle everything from successful 200 OK responses to custom 286 redirects using standard HTML attributes.
HTTP Status Code Processing Ranges
The htmx engine categorizes incoming responses into distinct groups based on their numeric status codes, applying different swap and event strategies to each.
Successful Responses (200–399)
Responses in the 200–399 range are considered successful. According to the source at src/htmx.js#L3320-L3324, htmx parses the response body and executes the swap strategy defined by hx-swap (defaulting to "innerHTML"). After swapping, the library fires the standard lifecycle events including htmx:afterSwap and htmx:afterSettle.
<div id="target" hx-get="/fragments/widget" hx-swap="outerHTML"></div>
When /fragments/widget returns 200, the HTML fragment replaces the content of #target, and the full event chain runs.
Error Responses (400 and Above)
Status codes 400 and above trigger the error handling path. As implemented in src/htmx.js#L4965-L4970, htmx calls triggerErrorEvent and passes an ErrorInfo object containing the status, URL, and optional error message. By default, no swap is performed, though the htmx:responseError event bubbles up for application-level handling.
<div id="msg" hx-get="/api/missing" hx-trigger="load"></div>
<script>
document.body.addEventListener('htmx:responseError', function (evt) {
const { error, status } = evt.detail;
document.querySelector('#msg').textContent = `Error ${status}: ${error}`;
});
</script>
If the server returns 404, the swap is skipped and the custom listener displays the failure message.
The Special Case of Status 286
Status code 286 serves as a built-in "response-target" shortcut for redirects. Located at src/htmx.js#L4894-L4899, this logic checks for a Location header or custom HX-Redirect header to replace the current page without a full reload. Importantly, htmx:responseError is not triggered for 286 responses.
<div hx-get="/redirect-me" hx-response-286="redirect"></div>
When the server replies with 286 and a Location header, htmx follows the redirect automatically.
Custom Status Code Matching
Developers can define fine-grained rules using hx-response-* attributes, which support regular expressions to match specific status codes. The configuration parsing resides around src/htmx.js#L4742-L4759, allowing arbitrary status codes to trigger specific swap behaviors or events independent of the default success/error dichotomy.
Internal Request Handling Flow
The request lifecycle follows a strict inspection sequence after the XHR load event fires.
- Status Inspection – The engine reads
xhr.statusand compares it against configured matchers and the standard ranges. - Success Path (200–399) – Invokes
handleResponse, which executesswapContentand triggers the normal lifecycle events. - Error Path (≥400) – Invokes
triggerErrorEvent, creating anErrorInfoobject that bubbles through the DOM so developers can listen forhtmx:responseError. - Special Handling – Status 286 and custom regex matches from
hx-response-*attributes can intercept the flow before standard success or error processing occurs.
Practical Implementation Examples
Swapping Content on Errors with hx-error-swap
Use the hx-error-swap attribute to force content insertion even when the server returns an error status code.
<div hx-get="/api/failure" hx-error-swap="innerHTML">
Loading…
</div>
Even for a 500 response, the response body will be swapped into the element because hx-error-swap overrides the default error skip logic.
Handling Specific Status Codes
You can combine standard event listeners with status-specific logic to handle partial failures or validation errors differently.
document.body.addEventListener('htmx:responseError', function (evt) {
if (evt.detail.xhr.status === 422) {
// Handle validation error specifically
alert('Validation failed');
} else if (evt.detail.xhr.status === 503) {
// Handle service unavailable
alert('Service temporarily unavailable');
}
});
Key Source Files and Extensions
| File | Role |
|---|---|
src/htmx.js |
Core engine containing handleResponse, triggerErrorEvent, and status-checking logic at lines 3320–3324, 4894–4899, and 4965–4970. |
dist/ext/response-targets.js |
Extension that adds support for the custom 286 status and HX-Redirect handling. |
dist/htmx.js |
Compiled, browser-ready version mirroring the core logic. |
Summary
- htmx categorizes status codes into success (200–399), error (≥400), and special cases (286) to determine swap behavior.
- Successful responses trigger
handleResponseandswapContent, firinghtmx:afterSwapandhtmx:afterSettle. - Error responses invoke
triggerErrorEventwith anErrorInfoobject containing status, URL, and message details. - Use
hx-error-swapto force content swapping on error status codes that would normally be skipped. - Status 286 bypasses error handling and processes redirects via
HX-RedirectorLocationheaders without firinghtmx:responseError. - Custom regex patterns in
hx-response-*attributes enable fine-grained status code handling beyond the default ranges.
Frequently Asked Questions
Does htmx swap content when it receives a 404 or 500 status code?
By default, no. Status codes 400 and above prevent content swapping unless you explicitly add the hx-error-swap attribute to the initiating element. Instead, htmx fires the htmx:responseError event, allowing your application logic to handle the failure gracefully.
How can I redirect the browser when the server returns a specific status code?
Configure your server to return status 286 along with a Location or HX-Redirect header. According to the source at src/htmx.js#L4894-L4899, htmx interprets this combination as a redirect instruction and updates window.location without triggering error events or performing a swap.
What is the difference between htmx:afterSwap and htmx:responseError events?
htmx:afterSwap fires immediately after htmx inserts content into the DOM during successful requests (status 200–399). Conversely, htmx:responseError fires for status codes 400 and above before any swap occurs (unless overridden by hx-error-swap), providing an ErrorInfo object with the failed request details.
Can I handle specific status codes like 422 differently in htmx?
Yes. Use the hx-response-* attribute pattern with regular expressions to match specific status codes. As implemented around src/htmx.js#L4742-L4759, these attributes allow you to define custom swap targets or behaviors for individual status codes, overriding the default success or error handling for those specific cases.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →