HTMX Cheat Sheet
Complete reference of HTMX attributes, triggers, swap modifiers, response headers, and JavaScript API. Search by name or browse by category.
hx-get
Core RequestsIssues a GET request to the given URL when the element is triggered
Syntax
hx-get="[url]"Examples
<button hx-get="/api/data">Load Data</button>GET request on button click
<div hx-get="/search" hx-trigger="load">Auto-load on page ready</div>GET on load
💡 Note: Default trigger is 'click' for most elements, 'change' for inputs, 'submit' for forms
hx-post
Core RequestsIssues a POST request to the given URL when triggered
Syntax
hx-post="[url]"Examples
<form hx-post="/submit">...</form>POST form data on submit
<button hx-post="/api/create" hx-target="#result">Create</button>POST with target
💡 Note: Submits form data (inputs within the form or hx-include'd elements)
hx-put
Core RequestsIssues a PUT request to the given URL when triggered
Syntax
hx-put="[url]"Examples
<button hx-put="/api/item/1" hx-include="#edit-form">Update</button>PUT with included form data
💡 Note: Use for full resource updates (REST convention)
hx-patch
Core RequestsIssues a PATCH request to the given URL when triggered
Syntax
hx-patch="[url]"Examples
<input hx-patch="/api/item/1" hx-trigger="change" name="status">PATCH on input change
💡 Note: Use for partial resource updates (REST convention)
hx-delete
Core RequestsIssues a DELETE request to the given URL when triggered
Syntax
hx-delete="[url]"Examples
<button hx-delete="/api/item/1" hx-target="closest li" hx-swap="outerHTML">Delete</button>Delete and remove element
<button hx-delete="/api/item/1" hx-confirm="Are you sure?">Delete</button>DELETE with confirmation
💡 Note: Combine with hx-confirm to prevent accidental deletions
hx-target
Targets & SwapsSpecifies the target element to swap the response content into
Syntax
hx-target="[css-selector | this | closest | find | next | previous]"Examples
<button hx-get="/data" hx-target="#result">Load</button>Target by ID
<button hx-get="/data" hx-target="this">Replace self</button>Target self
<button hx-get="/data" hx-target="closest .card">Target parent</button>Closest ancestor
<button hx-get="/data" hx-target="find .output">Target child</button>Find descendant
<button hx-get="/data" hx-target="next p">Target next sibling</button>Next element
💡 Note: Defaults to the element itself. Use CSS selectors or relative specifiers: this, closest, find, next, previous
hx-swap
Targets & SwapsControls how the response is swapped into the DOM (default: innerHTML)
Syntax
hx-swap="[innerHTML | outerHTML | beforebegin | afterbegin | beforeend | afterend | delete | none] [modifier]"Examples
<div hx-get="/data" hx-swap="innerHTML">Default inner swap</div>Replace inner content (default)
<div hx-get="/data" hx-swap="outerHTML">Replace element itself</div>Replace entire element
<ul hx-get="/items" hx-swap="beforeend">Append items to list</ul>Append to end
<div hx-get="/data" hx-swap="afterbegin">Prepend to start</div>Prepend to start
<div hx-get="/data" hx-swap="innerHTML swap:500ms">Smooth swap</div>With swap delay
<div hx-get="/data" hx-swap="innerHTML transition:true">Use view transitions</div>With View Transitions API
💡 Note: Modifiers: swap:<time>, settle:<time>, scroll:[top|bottom], show:[selector|top|bottom], focus-scroll:true, transition:true, ignoreTitle:true
hx-swap-oob
Targets & SwapsMarks content in the response to be swapped out-of-band (into a different element)
Syntax
hx-swap-oob="true | [swap-style] | [swap-style]:[css-selector]"Examples
<div id="notification" hx-swap-oob="true">Alert!</div>OOB swap by ID match
<div hx-swap-oob="outerHTML:#alert">New alert content</div>OOB with explicit target
💡 Note: Used in server responses to update multiple parts of the page simultaneously without JavaScript
hx-trigger
TriggersSpecifies the event that triggers the request (default: click for buttons, change for inputs, submit for forms)
Syntax
hx-trigger="[event] [modifier] [, event modifier ...]"Examples
<input hx-get="/search" hx-trigger="keyup changed delay:300ms">Search with debounce
<div hx-get="/feed" hx-trigger="load">Auto-load</div>Trigger on page load
<div hx-get="/poll" hx-trigger="every 5s">Poll every 5s</div>Polling
<div hx-get="/data" hx-trigger="click once">Load only once</div>Fire only once
<div hx-get="/data" hx-trigger="click throttle:2s">Throttle clicks</div>Throttle requests
<div hx-get="/data" hx-trigger="intersect">Lazy load on scroll</div>Trigger on viewport intersect
<div hx-get="/data" hx-trigger="revealed">On scroll reveal</div>Trigger when scrolled into view
<button hx-get="/data" hx-trigger="click from:body">Global click handler</button>Trigger from another element
💡 Note: Modifiers: once, changed, delay:<time>, throttle:<time>, from:<selector>, target:<selector>, consume, queue:[first|last|all|none]
hx-on
TriggersHandles any event with inline scripts (replaces x-on / v-on patterns without Alpine/Vue)
Syntax
hx-on:[event]="[javascript]" or hx-on::event="[javascript]"Examples
<button hx-get="/api" hx-on:htmx:before-request="this.disabled=true">Submit</button>Disable button before request
<div hx-on::after-swap="console.log('swapped!')">Content</div>Run JS after swap
<form hx-post="/submit" hx-on:submit="console.log('submitting')">...</form>Handle DOM events
💡 Note: hx-on:htmx:* handles HTMX lifecycle events. hx-on:[dom-event] handles standard DOM events. Use :: shorthand for htmx events.
hx-boost
Boosting & URLProgressively enhances anchor tags and forms to use HTMX (AJAX-style navigation)
Syntax
hx-boost="true | false"Examples
<body hx-boost="true">All links and forms use HTMX</body>Enable globally
<a href="/page" hx-boost="false">Normal navigation</a>Opt-out individual link
💡 Note: Converts <a> to GET requests and <form> to POST, swapping body innerHTML. Enables browser history automatically.
hx-push-url
Boosting & URLPushes the URL of the AJAX request to the browser's address bar (browser history entry)
Syntax
hx-push-url="true | false | [url]"Examples
<button hx-get="/page/2" hx-push-url="true">Next Page</button>Push request URL to history
<button hx-get="/page/2" hx-push-url="/custom-url">Custom URL in bar</button>Push custom URL
💡 Note: Creates a browser history entry, enabling back/forward navigation
hx-replace-url
Boosting & URLReplaces the current URL in the browser's address bar without creating a history entry
Syntax
hx-replace-url="true | false | [url]"Examples
<button hx-get="/filter?q=foo" hx-replace-url="true">Filter</button>Replace URL without history entry
💡 Note: Similar to hx-push-url but uses replaceState instead of pushState
hx-include
Forms & DataIncludes additional element values in the request
Syntax
hx-include="[css-selector]"Examples
<button hx-post="/submit" hx-include="#extra-fields">Submit with extras</button>Include form from elsewhere
<button hx-get="/search" hx-include="[name='q']">Search</button>Include by name attribute
<form hx-post="/submit" hx-include="closest form">Include parent form</form>Include closest form
💡 Note: Use CSS selectors. The element and all its inputs are included in the request
hx-params
Forms & DataFilters which parameters are submitted with a request
Syntax
hx-params="* | none | not [list] | [list]"Examples
<form hx-post="/submit" hx-params="not email">Exclude email field</form>Exclude specific field
<form hx-post="/submit" hx-params="name,age">Only name and age</form>Whitelist fields
<form hx-post="/submit" hx-params="none">No parameters</form>Send no params
💡 Note: * = all params (default), none = no params, not [list] = exclude listed params
hx-vals
Forms & DataAdds additional values to the request parameters (as JSON or js:expression)
Syntax
hx-vals='{"key": "value"}' or hx-vals="js:{key: expression}"Examples
<button hx-get="/search" hx-vals='{"category": "books"}'>Books</button>Add static JSON values
<button hx-get="/api" hx-vals="js:{ts: Date.now()}">With timestamp</button>Dynamic JS values
💡 Note: JSON values are merged into the request parameters. Use js: prefix for dynamic values.
hx-encoding
Forms & DataChanges the request encoding type
Syntax
hx-encoding="multipart/form-data"Examples
<form hx-post="/upload" hx-encoding="multipart/form-data"><input type="file" name="file"></form>File upload form
💡 Note: Use multipart/form-data for file uploads. Default is application/x-www-form-urlencoded.
hx-indicator
IndicatorsSpecifies the element to show during a request (using the htmx-request CSS class)
Syntax
hx-indicator="[css-selector]"Examples
<button hx-get="/data" hx-indicator="#spinner">Load
<span id="spinner" class="htmx-indicator">⏳</span></button>Show spinner during request
<button hx-get="/data" hx-indicator="closest .loading-area">Load</button>Target ancestor
💡 Note: The .htmx-indicator class is opacity:0 by default; during requests it becomes opacity:1
hx-disabled-elt
IndicatorsDisables the specified element during a request
Syntax
hx-disabled-elt="[css-selector | this | closest | find]"Examples
<button hx-post="/submit" hx-disabled-elt="this">Submit</button>Disable button while submitting
<form hx-post="/submit" hx-disabled-elt="find input, find button">Submit</form>Disable all inputs in form
💡 Note: Adds the disabled attribute during the request, removes it when complete
hx-history
HistoryControls browser history caching for the element's content
Syntax
hx-history="false"Examples
<div hx-history="false">Sensitive content (not cached in history)</div>Disable history snapshot for sensitive data
💡 Note: Use false to prevent sensitive content from being stored in the browser's history cache
hx-history-elt
HistoryMarks an element as the history snapshot target (what gets saved in browser history)
Syntax
hx-history-eltExamples
<div id="main" hx-history-elt>Main content area</div>Use this element for history snapshots
💡 Note: Only one element per page. Defaults to <body> if not specified.
hx-sync
SyncControls how requests from multiple elements are synchronized
Syntax
hx-sync="[selector]:[strategy]"Examples
<input hx-get="/search" hx-sync="this:replace">Fast typing</input>Cancel previous, replace with new
<button hx-post="/submit" hx-sync="closest form:abort">Submit</button>Abort if form is already submitting
<div hx-get="/data" hx-sync="form:queue last">Queue last request</div>Queue only the last request
💡 Note: Strategies: drop (ignore), abort, replace, queue [first|last|all]. Default selector: this
hx-confirm
ConfirmShows a confirm() dialog before issuing a request
Syntax
hx-confirm="[message]"Examples
<button hx-delete="/item/1" hx-confirm="Delete this item?">Delete</button>Confirm before delete
<form hx-post="/submit" hx-confirm="Submit form?">Submit</form>Confirm before form submit
💡 Note: Uses the browser's native confirm() dialog. For custom dialogs, use htmx:confirm event.
hx-select
ConfirmSelects a subset of the server response to swap in
Syntax
hx-select="[css-selector]"Examples
<button hx-get="/full-page" hx-select="#content">Load only #content</button>Extract partial response
💡 Note: Useful when the server returns a full page but you only need part of it
hx-select-oob
ConfirmSelects content from a response to swap in out-of-band (into elements matching by ID)
Syntax
hx-select-oob="[css-selector], [css-selector]:[swap-style]"Examples
<button hx-get="/page" hx-select="#main" hx-select-oob="#sidebar">Update both</button>Select main and OOB update sidebar
💡 Note: Combines hx-select with OOB swapping in a single request
hx-ext
ExtensionsEnables HTMX extensions for enhanced functionality
Syntax
hx-ext="[extension-name], [extension-name]"Examples
<body hx-ext="json-enc">Use JSON encoding globally</body>JSON encoding extension
<div hx-ext="preload">Preload links on hover</div>Preload extension
<div hx-ext="ws" ws-connect="/chat">WebSocket chat</div>WebSocket extension
<div hx-ext="sse" sse-connect="/events">Server-Sent Events</div>SSE extension
💡 Note: Popular extensions: json-enc, ws (WebSockets), sse (Server-Sent Events), preload, head-support, class-tools, morphdom-swap
HX-Trigger
Response HeadersServer response header: triggers client-side events after swap
Syntax
HX-Trigger: eventName
HX-Trigger: {"event1": "val1", "event2": "val2"}Examples
HX-Trigger: showModalTrigger simple event
HX-Trigger: {"showMessage": "Item saved!"}Trigger event with data
HX-Trigger-After-Swap: refreshCartTrigger after swap completes
HX-Trigger-After-Settle: updateCountTrigger after settle completes
💡 Note: Variants: HX-Trigger (immediately), HX-Trigger-After-Swap, HX-Trigger-After-Settle
HX-Redirect
Response HeadersServer response header: redirects the client to a new URL (full page reload)
Syntax
HX-Redirect: [url]Examples
HX-Redirect: /dashboardRedirect to dashboard after action
💡 Note: Causes a full page navigation, not an HTMX swap. Use HX-Push-Url for soft navigation.
HX-Refresh
Response HeadersServer response header: triggers a full page refresh
Syntax
HX-Refresh: trueExamples
HX-Refresh: trueForce full page reload
💡 Note: Causes the browser to do a full reload of the current page
HX-Retarget
Response HeadersServer response header: changes the target element for the response swap
Syntax
HX-Retarget: [css-selector]Examples
HX-Retarget: #error-areaRedirect response to error container on failure
💡 Note: Overrides the hx-target attribute from the client. Useful for error handling.
HX-Reswap
Response HeadersServer response header: changes the swap method for the response
Syntax
HX-Reswap: [swap-style]Examples
HX-Reswap: outerHTMLOverride client's swap style
HX-Reswap: nonePrevent swap (but still trigger events)
💡 Note: Overrides the hx-swap attribute from the client
HX-Push-Url
Response HeadersServer response header: pushes a URL to the browser history stack
Syntax
HX-Push-Url: [url]Examples
HX-Push-Url: /items/123Push canonical URL to history
HX-Push-Url: falsePrevent URL push even if hx-push-url is set
💡 Note: Overrides hx-push-url from the client. Use false to suppress client-side push.
HX-Location
Response HeadersServer response header: pushes a URL to the history stack without page reload (AJAX navigation)
Syntax
HX-Location: [url] or HX-Location: {"path": "/url", "target": "#content"}Examples
HX-Location: /new-pageSoft navigate to new page
{"path": "/new-page", "target": "#main", "swap": "innerHTML"}Full control over location swap
💡 Note: More powerful than HX-Redirect — does a client-side swap with history update, no full reload
htmx-request
CSS ClassesCSS class added to the element making a request (and its indicator) while a request is in flight
Syntax
.htmx-request { /* styles during request */ }Examples
.htmx-request { opacity: 0.5; }
.htmx-request .spinner { display: inline; }Dim element during request
.htmx-indicator {
opacity: 0;
transition: opacity 200ms ease-in;
}
.htmx-request .htmx-indicator {
opacity: 1;
}Fade in spinner
💡 Note: Applied to both the requesting element and any hx-indicator targets
htmx-swapping
CSS ClassesCSS class applied to target element during the swap phase
Syntax
.htmx-swapping { /* styles during swap */ }Examples
.htmx-swapping {
opacity: 0;
transition: opacity 100ms ease-out;
}Fade out before new content appears
💡 Note: Use with swap:Xms modifier on hx-swap to control the duration
htmx-settling
CSS ClassesCSS class applied to target element during the settle phase (after new content is inserted)
Syntax
.htmx-settling { /* styles during settle */ }Examples
.htmx-added {
opacity: 0;
}
.htmx-settling .htmx-added {
opacity: 1;
transition: opacity 300ms ease-in;
}Fade in new content
💡 Note: The settle phase allows CSS transitions to run after new content is inserted
htmx-added
CSS ClassesCSS class briefly added to new content after it is swapped in, enabling entry animations
Syntax
.htmx-added { /* entry animation start state */ }Examples
.htmx-added {
opacity: 0;
transform: translateY(-10px);
}
.htmx-added {
transition: opacity 300ms, transform 300ms;
}Slide-in animation for new items
💡 Note: Added immediately after swap, removed after settle. Perfect for list item animations.
htmx.ajax()
JavaScript APIIssues an HTMX-style AJAX request programmatically from JavaScript
Syntax
htmx.ajax(verb, path, context)Examples
htmx.ajax('GET', '/api/data', '#target')GET and swap into #target
htmx.ajax('POST', '/api/create', {
target: '#result',
swap: 'beforeend',
values: { name: 'test' }
})POST with target and values
htmx.ajax('DELETE', '/api/item/1', {
target: 'closest li',
swap: 'delete'
})DELETE with closest selector
💡 Note: Returns a Promise. Context can be a CSS selector string or an object with target, swap, values, source, etc.
htmx.trigger()
JavaScript APITriggers an event on an element
Syntax
htmx.trigger(element, eventName, detail)Examples
htmx.trigger('#my-form', 'submit')Trigger form submission
htmx.trigger(document.body, 'refreshData', { id: 42 })Trigger custom event with data
💡 Note: Useful for triggering hx-trigger events programmatically
htmx.process()
JavaScript APIProcesses an element and its children, initializing HTMX attributes on dynamically added content
Syntax
htmx.process(element)Examples
const div = document.createElement('div');
div.innerHTML = '<button hx-get="/api">Load</button>';
document.body.appendChild(div);
htmx.process(div);Initialize HTMX on dynamically injected HTML
💡 Note: Required when you add HTMX-attributed HTML to the DOM outside of HTMX itself (e.g., via vanilla JS)
htmx.on()
JavaScript APIAttaches an event listener to an element for HTMX or DOM events
Syntax
htmx.on(target, eventName, callback) or htmx.on(eventName, callback)Examples
htmx.on('htmx:afterSwap', function(evt) {
console.log('Swapped:', evt.detail);
});Listen to HTMX lifecycle event
htmx.on('#form', 'htmx:beforeRequest', function(evt) {
evt.preventDefault(); // cancel
});Cancel a request
💡 Note: Returns the event listener function for later removal. Works like addEventListener but with HTMX event name normalization.
hx-swap Values Reference
All 8 swap strategies for controlling how the server response enters the DOM.
| Value | Description |
|---|---|
| innerHTML | Replace inner HTML of target (default) |
| outerHTML | Replace entire target element |
| beforebegin | Insert before the target element |
| afterbegin | Insert as first child of target |
| beforeend | Insert as last child of target |
| afterend | Insert after the target element |
| delete | Delete target regardless of response |
| none | No swap — response is discarded (events still fire) |
Modifiers can be appended to any swap value: swap:<time>, settle:<time>, scroll:[top|bottom], show:selector, transition:true, focus-scroll:true
hx-trigger Modifiers Reference
Modifiers can be chained after the event name in hx-trigger.
| Modifier | Description |
|---|---|
| once | Trigger only once, then stop listening |
| changed | Only trigger if the element's value has changed |
| delay:<time> | Wait before triggering (debounce). E.g. delay:500ms |
| throttle:<time> | Throttle: minimum time between triggers. E.g. throttle:2s |
| from:<selector> | Listen for event on a different element. E.g. from:window |
| target:<selector> | Only trigger if event target matches selector |
| consume | Consume the event, preventing it from bubbling |
| queue:[first|last|all|none] | Queue strategy for multiple requests |
| intersect | Fires when element enters viewport (Intersection Observer) |
| revealed | Fires when element is scrolled into view |
| load | Fires once when element is loaded |
| every <time> | Polling: trigger at an interval. E.g. every 5s |
What is HTMX?
🌐 Hypermedia-Driven Development
HTMX is a lightweight JavaScript library (~14kb min.gz) that extends HTML with custom attributes to make any element capable of issuing HTTP requests. Instead of writing JavaScript to fetch data and update the DOM, you describe the behavior directly in HTML.
The core idea is hypermedia — the server sends HTML fragments, not JSON, and the browser swaps them into the page. This is how the web was originally designed, but now turbocharged for modern interactivity.
<!-- This button makes a real HTTP GET, swaps the response into #content -->
<button hx-get="/api/data" hx-target="#content">Load</button>
🚀 HTMX vs React/Vue SPAs
HTMX approach
- ✅ Server renders HTML (any language/framework)
- ✅ No JSON serialization/deserialization layer
- ✅ No client-side state management
- ✅ Works with any backend (PHP, Rails, Django, Laravel...)
- ✅ Progressive enhancement — works without JS
- ✅ ~14kb bundle, zero build step required
- ⚠️ Less suited for highly interactive client UIs
React/Vue SPA approach
- ✅ Rich client-side interactivity
- ✅ Instant UI updates without server round-trips
- ✅ Component reusability and large ecosystem
- ⚠️ JSON API layer required
- ⚠️ Client-side state management complexity
- ⚠️ Larger bundles, build pipeline required
- ⚠️ SEO requires SSR setup
Rule of thumb: Use HTMX for content-heavy apps where most interactivity is CRUD operations. Use React/Vue when you need a desktop-app-like client experience with complex local state.
⚖️ HTMX vs Alpine.js vs Stimulus
| Feature | HTMX | Alpine.js | Stimulus |
|---|---|---|---|
| Primary role | Server-driven partial page updates | Client-side reactivity & state | Connecting JS to existing HTML |
| State lives in | Server | Browser (x-data) | JS controllers |
| Server response | HTML fragments | Typically JSON | Either (JSON or HTML) |
| Bundle size | ~14kb | ~16kb | ~80kb |
| Best for | CRUD-heavy apps, forms, tables | Dropdowns, tabs, local UI state | Hotwire/Turbo, Rails apps |
| Works together? | Yes — pairs great with Alpine.js | Yes — use with HTMX for UI state | Yes — separate concerns cleanly |
| No-JS fallback | ✅ (forms still submit) | ❌ | Partial |
Common stack: HTMX for server requests + Alpine.js for local client UI state = covers 90% of web app needs without React.
⚡ Getting Started with HTMX
1. Add the Script Tag
<script src="https://unpkg.com/htmx.org@2.0.4"></script>2. Your First HTMX Button
<button hx-get="/api/hello" hx-target="#result">Say Hello</button>
<div id="result"></div>3. Server Returns HTML (not JSON)
# Flask / Django / Laravel / Rails — just return HTML
return "<p>Hello from the server!</p>"HTMX Philosophy: "The simplest tools that do the job." HTMX reclaims the power of hypermedia — the original architecture of the web — while keeping your HTML the source of truth. No state management, no virtual DOM, no build step. Just HTML and a server that knows how to render it. 🌐
HTMX Pro Tips
Common Patterns
- Use
hx-boost="true"on<body>for free SPA-like navigation - Combine
hx-trigger="keyup changed delay:300ms"for search inputs - Use
hx-swap-oob="true"to update counters/notifications in a single response - Add
hx-disabled-elt="this"to buttons to prevent double-submission - Use
hx-indicatorwith CSS transitions for smooth loading states
HTMX Best Practices
- Return only the HTML fragment you need (not a full page)
- Use
HX-Triggerresponse header to coordinate cross-page updates - Keep IDs stable — HTMX targets elements by CSS selector
- Pair with Alpine.js for purely client-side UI state (modals, tabs)
- Test with JS disabled first — good HTMX apps degrade gracefully
- Use
hx-confirmfor destructive actions