CtrlK
BlogDocsLog inGet started
Tessl Logo

alonso-skills/htmx

Implements HTMX interactions, configures swap behaviors, debugs hx-* requests, and builds hypermedia-driven UI components. Use when tasks involve hx-* attributes, HTMX AJAX requests, swap strategies, server-sent events, WebSockets, or hypermedia-driven UIs.

95

Quality

95%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

requests.mdreferences/

description:
HTMX request lifecycle, headers, parameters, security, and caching.
globs:
*.html

HTMX Requests

How HTMX issues requests, what gets sent, and how to control the lifecycle.

Contents

  • Request Lifecycle
  • Request Headers
  • Response Headers
  • Parameters & Values
  • File Upload
  • CSRF Protection
  • CORS
  • Caching
  • Security
  • Validation
  • Confirmation & Prompts

Request Lifecycle

The order of operations for an HTMX request:

  1. Element is triggered (event fires, passes filters)
  2. Values are gathered from the element and included elements
  3. htmx-request class is applied to the element (or hx-indicator target)
  4. AJAX request is issued
  5. Upon response, htmx-swapping class is added to the target
  6. Optional swap delay (swap:<time> modifier)
  7. Old content is swapped out, new content is swapped in
  8. htmx-settling class is applied to the target
  9. Settle delay (default 20ms)
  10. DOM is settled, htmx-added class removed from new content
  11. All HTMX classes are removed

CSS Classes During Lifecycle

ClassApplied ToWhen
htmx-requestTriggering element or hx-indicator targetDuring the request
htmx-swappingTarget elementAfter response, before swap
htmx-settlingTarget elementAfter swap, before settle completes
htmx-addedNew content elementsAfter swap, removed after settle

Use these classes for loading indicators, animations, and transitions.

Request Headers

HTMX automatically includes these headers with every request:

HeaderValuePurpose
HX-BoostedtruePresent when request is via hx-boost
HX-Current-URLCurrent browser URLLets server know the user's current page
HX-History-Restore-RequesttruePresent when request is for history cache miss restoration
HX-PromptUser's responsePresent when hx-prompt was used
HX-RequesttrueAlways present on HTMX requests
HX-TargetTarget element's idThe target element for the response
HX-TriggerTriggering element's idThe element that triggered the request
HX-Trigger-NameTriggering element's nameThe name attribute of the triggering element

Server-Side Detection

Use HX-Request: true to detect HTMX requests and return partial HTML instead of full pages:

# Python/Flask example
if request.headers.get('HX-Request'):
    return render_template('partial.html')
return render_template('full_page.html')
// Node.js/Express example
if (req.headers['hx-request']) {
    return res.render('partial');
}
res.render('full_page');

Response Headers

The server can control client behavior using these response headers:

HeaderEffect
HX-LocationClient-side redirect (AJAX, no full page reload). Value: URL string or JSON {"path": "/url", "target": "#el"}
HX-Push-UrlPush URL into browser history. Value: URL string or false
HX-RedirectFull-page client-side redirect (non-AJAX)
HX-RefreshFull page refresh. Value: true
HX-Replace-UrlReplace current URL in location bar (no new history entry). Value: URL string or false
HX-ReswapOverride the hx-swap value for this response. Value: any valid swap strategy
HX-RetargetOverride the hx-target value. Value: CSS selector
HX-ReselectOverride the hx-select value. Value: CSS selector
HX-TriggerTrigger client-side events. Value: event name or JSON {"event": {"key": "value"}}
HX-Trigger-After-SettleTrigger events after the settle phase
HX-Trigger-After-SwapTrigger events after the swap phase

Triggering Client Events from Server

HX-Trigger: myEvent

With data:

HX-Trigger: {"showMessage": {"level": "info", "message": "Item saved!"}}

Multiple events:

HX-Trigger: {"event1": null, "event2": {"key": "value"}}

No POST/Redirect/GET Required

HTMX does not need the traditional POST/Redirect/GET pattern. After a successful POST, the server can return new HTML directly — no 302 redirect needed.

204 No Content

A 204 No Content response performs no swap. Useful for fire-and-forget requests.

Error Responses

  • Non-2xx responses (except 204) trigger the htmx:responseError event
  • Network failures trigger the htmx:sendError event

Important: Response headers (HX-Trigger, HX-Retarget, etc.) are NOT processed on 3xx redirect responses. Use alternative status codes when returning htmx-specific headers.

Response Handling Configuration

Override default behavior per status code using htmx.config.responseHandling:

htmx.config.responseHandling = [
    { code: "204", swap: false },
    { code: "[23]..", swap: true },
    { code: "422", swap: true, error: true },
    { code: "[45]..", swap: false, error: true },
    // Custom: swap 404 responses into a specific target
    { code: "404", swap: true, target: "#error-container" }
];

Fields per entry:

FieldTypePurpose
codeString (regex)Status code pattern to match
swapBooleanWhether to swap content into DOM
errorBooleanWhether to treat as error (fires error events)
ignoreTitleBooleanSkip <title> updates
selectStringCSS selector for response content
targetStringAlternative target selector
swapOverrideStringCustom swap strategy

Parameters & Values

What Gets Sent

ScenarioIncluded Values
Input/select/textarea triggers requestIts own name/value pair
Element inside a form (non-GET)All form input values
hx-include specifiedValues from the targeted elements
hx-vals specifiedAdditional JSON key-value pairs
hx-params specifiedFiltered subset of values

Parameter Encoding

MethodEncoding
GET, DELETEURL query string parameters
POST, PUT, PATCHURL-encoded form body (application/x-www-form-urlencoded)
With hx-encoding="multipart/form-data"Multipart form body

Programmatic Parameter Modification

Use the htmx:configRequest event:

<div hx-get="/data" hx-on:htmx:config-request="event.detail.parameters.extra = 'value'">
    Load
</div>

Or globally:

document.body.addEventListener('htmx:configRequest', function(event) {
    event.detail.parameters['auth'] = getToken();
    event.detail.headers['X-Custom'] = 'value';
});

File Upload

Set hx-encoding="multipart/form-data" to enable file uploads:

<form hx-post="/upload" hx-encoding="multipart/form-data">
    <input type="file" name="document" />
    <button type="submit">Upload</button>
</form>

Track upload progress via the htmx:xhr:progress event:

<form hx-post="/upload" hx-encoding="multipart/form-data"
      hx-on:htmx:xhr:progress="updateProgress(event)">
    <input type="file" name="document" />
    <progress id="progress" value="0" max="100"></progress>
    <button type="submit">Upload</button>
</form>

<script>
    function updateProgress(event) {
        var progress = event.detail.loaded / event.detail.total * 100;
        document.getElementById('progress').value = progress;
    }
</script>

CSRF Protection

Include Token via hx-headers

Place on <body> or a high-level element to inherit to all requests:

<body hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token }}"}'>
    ...
</body>

Include Token via htmx:configRequest

document.body.addEventListener('htmx:configRequest', function(event) {
    event.detail.headers['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').content;
});

Framework Patterns

Most server frameworks auto-insert CSRF tokens into <form> elements. For non-form HTMX requests, use one of the above approaches.

CORS

For cross-origin requests, configure the server to allow HTMX headers:

Access-Control-Allow-Headers: HX-Request, HX-Trigger, HX-Trigger-Name, HX-Target, HX-Current-URL, HX-Boosted, HX-Prompt
Access-Control-Expose-Headers: HX-Location, HX-Push-Url, HX-Redirect, HX-Refresh, HX-Replace-Url, HX-Reswap, HX-Retarget, HX-Reselect, HX-Trigger, HX-Trigger-After-Settle, HX-Trigger-After-Swap

Note: htmx.config.selfRequestsOnly = true (default) restricts requests to the same domain. Set to false for cross-origin requests, or use the htmx:validateUrl event for fine-grained control.

Caching

HTMX respects standard HTTP caching headers:

HeaderBehavior
Last-ModifiedHTMX sends If-Modified-Since on subsequent requests
ETagHTMX sends If-None-Match on subsequent requests
Cache-ControlStandard browser caching applies

Preventing Mixed Cache

Use Vary: HX-Request to prevent caching HTMX partial responses as full pages:

Vary: HX-Request

Cache Busting

htmx.config.getCacheBusterParam = true;

Appends a unique org.htmx.cache-buster query parameter to GET requests.

Security

Core Rule

Always escape all user-supplied content server-side. Whitelist allowed HTML tags/attributes rather than blacklisting.

hx-disable

Prevents HTMX processing on an element and all children. Use to protect user-content areas:

<div hx-disable>
    {{ user_generated_content }}
</div>

Content Security Policy (CSP)

<meta http-equiv="Content-Security-Policy" content="default-src 'self';">

Configuration for Hardening

// Restrict to same-origin requests only (default: true)
htmx.config.selfRequestsOnly = true;

// Disable eval-dependent features
htmx.config.allowEval = false;

// Disable script tag processing in responses
htmx.config.allowScriptTags = false;

// Disable history cache (no localStorage usage)
htmx.config.historyCacheSize = 0;

URL Validation

Use htmx:validateUrl to inspect and block requests:

document.body.addEventListener('htmx:validateUrl', function(event) {
    if (!event.detail.sameHost) {
        event.preventDefault(); // Block cross-origin request
    }
});

Validation

HTMX integrates with the HTML5 Validation API. If a form element is invalid, the request is blocked.

<form hx-post="/submit">
    <input name="email" type="email" required />
    <button type="submit">Submit</button>
</form>

Validation on Non-Form Elements

<input hx-get="/check" hx-validate="true" required pattern="[a-z]+" />

Validation Events

EventWhen
htmx:validation:validateBefore checkValidity() is called
htmx:validation:failedAfter a validation failure
htmx:validation:haltedWhen a request is halted due to validation

Browser Validation Alerts

htmx.config.reportValidityOfForms = true; // default: false

Confirmation & Prompts

Simple Confirm

<button hx-delete="/item/1" hx-confirm="Delete this item?">Delete</button>

Custom Confirm Dialog

Use the htmx:confirm event for non-native dialogs:

document.body.addEventListener('htmx:confirm', function(event) {
    event.preventDefault(); // Halt the request

    showCustomDialog(event.detail.question).then(function(confirmed) {
        if (confirmed) {
            event.detail.issueRequest(); // Continue the request
        }
    });
});

Prompt

<button hx-post="/rename" hx-prompt="Enter new name:">Rename</button>

The user's response is available in the HX-Prompt request header.

references

attributes.md

events-api.md

extensions.md

gotchas.md

patterns.md

REFERENCE.md

requests.md

swapping.md

SKILL.md

tile.json