Skip to content

Instantly share code, notes, and snippets.

@khromov
Last active April 27, 2025 23:20
Show Gist options
  • Save khromov/c2eae2ca594f4987e94728ed172deaaf to your computer and use it in GitHub Desktop.
Save khromov/c2eae2ca594f4987e94728ed172deaaf to your computer and use it in GitHub Desktop.
llms.txt file for htmx
# api.md
```md
+++
title = "Javascript API"
description = """\
This documentation describes the JavaScript API for htmx, including methods and properties for configuring the \
behavior of htmx, working with CSS classes, AJAX requests, event handling, and DOM manipulation. The API provides \
helper functions primarily intended for extension development and event management."""
+++
While it is not a focus of the library, htmx does provide a small API of helper methods, intended mainly for [extension development](https://htmx.org/extensions) or for working with [events](@/events.md).
The [hyperscript](https://hyperscript.org) project is intended to provide more extensive scripting support
for htmx-based applications.
### Method - `htmx.addClass()` {#addClass}
This method adds a class to the given element.
##### Parameters
* `elt` - the element to add the class to
* `class` - the class to add
or
* `elt` - the element to add the class to
* `class` - the class to add
* `delay` - delay (in milliseconds ) before class is added
##### Example
\`\`\`js
// add the class 'myClass' to the element with the id 'demo'
htmx.addClass(htmx.find('#demo'), 'myClass');
// add the class 'myClass' to the element with the id 'demo' after 1 second
htmx.addClass(htmx.find('#demo'), 'myClass', 1000);
\`\`\`
### Method - `htmx.ajax()` {#ajax}
Issues an htmx-style AJAX request. This method returns a Promise, so a callback can be executed after the content has been inserted into the DOM.
##### Parameters
* `verb` - 'GET', 'POST', etc.
* `path` - the URL path to make the AJAX
* `element` - the element to target (defaults to the `body`)
or
* `verb` - 'GET', 'POST', etc.
* `path` - the URL path to make the AJAX
* `selector` - a selector for the target
or
* `verb` - 'GET', 'POST', etc.
* `path` - the URL path to make the AJAX
* `context` - a context object that contains any of the following
* `source` - the source element of the request, `hx-*` attrs which affect the request will be resolved against that element and its ancestors
* `event` - an event that "triggered" the request
* `handler` - a callback that will handle the response HTML
* `target` - the target to swap the response into
* `swap` - how the response will be swapped in relative to the target
* `values` - values to submit with the request
* `headers` - headers to submit with the request
* `select` - allows you to select the content you want swapped from a response
##### Example
\`\`\`js
// issue a GET to /example and put the response HTML into #myDiv
htmx.ajax('GET', '/example', '#myDiv')
// issue a GET to /example and replace #myDiv with the response
htmx.ajax('GET', '/example', {target:'#myDiv', swap:'outerHTML'})
// execute some code after the content has been inserted into the DOM
htmx.ajax('GET', '/example', '#myDiv').then(() => {
// this code will be executed after the 'htmx:afterOnLoad' event,
// and before the 'htmx:xhr:loadend' event
console.log('Content inserted successfully!');
});
\`\`\`
### Method - `htmx.closest()` {#closest}
Finds the closest matching element in the given elements parentage, inclusive of the element
##### Parameters
* `elt` - the element to find the selector from
* `selector` - the selector to find
##### Example
\`\`\`js
// find the closest enclosing div of the element with the id 'demo'
htmx.closest(htmx.find('#demo'), 'div');
\`\`\`
### Property - `htmx.config` {#config}
A property holding the configuration htmx uses at runtime.
Note that using a [meta tag](@/docs.md#config) is the preferred mechanism for setting these properties.
##### Properties
* `attributesToSettle:["class", "style", "width", "height"]` - array of strings: the attributes to settle during the settling phase
* `refreshOnHistoryMiss:false` - boolean: if set to `true` htmx will issue a full page refresh on history misses rather than use an AJAX request
* `defaultSettleDelay:20` - int: the default delay between completing the content swap and settling attributes
* `defaultSwapDelay:0` - int: the default delay between receiving a response from the server and doing the swap
* `defaultSwapStyle:'innerHTML'` - string: the default swap style to use if [`hx-swap`](@/attributes/hx-swap.md) is omitted
* `historyCacheSize:10` - int: the number of pages to keep in `localStorage` for history support
* `historyEnabled:true` - boolean: whether or not to use history
* `includeIndicatorStyles:true` - boolean: if true, htmx will inject a small amount of CSS into the page to make indicators invisible unless the `htmx-indicator` class is present
* `indicatorClass:'htmx-indicator'` - string: the class to place on indicators when a request is in flight
* `requestClass:'htmx-request'` - string: the class to place on triggering elements when a request is in flight
* `addedClass:'htmx-added'` - string: the class to temporarily place on elements that htmx has added to the DOM
* `settlingClass:'htmx-settling'` - string: the class to place on target elements when htmx is in the settling phase
* `swappingClass:'htmx-swapping'` - string: the class to place on target elements when htmx is in the swapping phase
* `allowEval:true` - boolean: allows the use of eval-like functionality in htmx, to enable `hx-vars`, trigger conditions & script tag evaluation. Can be set to `false` for CSP compatibility.
* `allowScriptTags:true` - boolean: allows script tags to be evaluated in new content
* `inlineScriptNonce:''` - string: the [nonce](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/nonce) to add to inline scripts
* `inlineStyleNonce:''` - string: the [nonce](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/nonce) to add to inline styles
* `withCredentials:false` - boolean: allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates
* `timeout:0` - int: the number of milliseconds a request can take before automatically being terminated
* `wsReconnectDelay:'full-jitter'` - string/function: the default implementation of `getWebSocketReconnectDelay` for reconnecting after unexpected connection loss by the event code `Abnormal Closure`, `Service Restart` or `Try Again Later`
* `wsBinaryType:'blob'` - string: the [the type of binary data](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) being received over the WebSocket connection
* `disableSelector:"[hx-disable], [data-hx-disable]"` - array of strings: htmx will not process elements with this attribute on it or a parent
* `disableInheritance:false` - boolean: If it is set to `true`, the inheritance of attributes is completely disabled and you can explicitly specify the inheritance with the [hx-inherit](@/attributes/hx-inherit.md) attribute.
* `scrollBehavior:'instant'` - string: the scroll behavior when using the [show](@/attributes/hx-swap.md#scrolling-scroll-show) modifier with `hx-swap`. The allowed values are `instant` (scrolling should happen instantly in a single jump), `smooth` (scrolling should animate smoothly) and `auto` (scroll behavior is determined by the computed value of [scroll-behavior](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior)).
* `defaultFocusScroll:false` - boolean: if the focused element should be scrolled into view, can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier
* `getCacheBusterParam:false` - boolean: if set to true htmx will append the target element to the `GET` request in the format `org.htmx.cache-buster=targetElementId`
* `globalViewTransitions:false` - boolean: if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content.
* `methodsThatUseUrlParams:["get", "delete"]` - array of strings: htmx will format requests with these methods by encoding their parameters in the URL, not the request body
* `selfRequestsOnly:true` - boolean: whether to only allow AJAX requests to the same domain as the current document
* `ignoreTitle:false` - boolean: if set to `true` htmx will not update the title of the document when a `title` tag is found in new content
* `scrollIntoViewOnBoost:true` - boolean: whether or not the target of a boosted element is scrolled into the viewport. If `hx-target` is omitted on a boosted element, the target defaults to `body`, causing the page to scroll to the top.
* `triggerSpecsCache:null` - object: the cache to store evaluated trigger specifications into, improving parsing performance at the cost of more memory usage. You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
* `htmx.config.responseHandling:[...]` - HtmxResponseHandlingConfig[]: the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error
* `htmx.config.allowNestedOobSwaps:true` - boolean: whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps).
##### Example
\`\`\`js
// update the history cache size to 30
htmx.config.historyCacheSize = 30;
\`\`\`
### Property - `htmx.createEventSource` {#createEventSource}
A property used to create new [Server Sent Event](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/sse/README.md) sources. This can be updated
to provide custom SSE setup.
##### Value
* `func(url)` - a function that takes a URL string and returns a new `EventSource`
##### Example
\`\`\`js
// override SSE event sources to not use credentials
htmx.createEventSource = function(url) {
return new EventSource(url, {withCredentials:false});
};
\`\`\`
### Property - `htmx.createWebSocket` {#createWebSocket}
A property used to create new [WebSocket](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ws/README.md). This can be updated
to provide custom WebSocket setup.
##### Value
* `func(url)` - a function that takes a URL string and returns a new `WebSocket`
##### Example
\`\`\`js
// override WebSocket to use a specific protocol
htmx.createWebSocket = function(url) {
return new WebSocket(url, ['wss']);
};
\`\`\`
### Method - `htmx.defineExtension()` {#defineExtension}
Defines a new htmx [extension](https://htmx.org/extensions).
##### Parameters
* `name` - the extension name
* `ext` - the extension definition
##### Example
\`\`\`js
// defines a silly extension that just logs the name of all events triggered
htmx.defineExtension("silly", {
onEvent : function(name, evt) {
console.log("Event " + name + " was triggered!")
}
});
\`\`\`
### Method - `htmx.find()` {#find}
Finds an element matching the selector
##### Parameters
* `selector` - the selector to match
or
* `elt` - the root element to find the matching element in, inclusive
* `selector` - the selector to match
##### Example
\`\`\`js
// find div with id my-div
var div = htmx.find("#my-div")
// find div with id another-div within that div
var anotherDiv = htmx.find(div, "#another-div")
\`\`\`
### Method - `htmx.findAll()` {#findAll}
Finds all elements matching the selector
##### Parameters
* `selector` - the selector to match
or
* `elt` - the root element to find the matching elements in, inclusive
* `selector` - the selector to match
##### Example
\`\`\`js
// find all divs
var allDivs = htmx.findAll("div")
// find all paragraphs within a given div
var allParagraphsInMyDiv = htmx.findAll(htmx.find("#my-div"), "p")
\`\`\`
### Method - `htmx.logAll()` {#logAll}
Log all htmx events, useful for debugging.
##### Example
\`\`\`js
htmx.logAll();
\`\`\`
### Method - `htmx.logNone()` {#logNone}
Log no htmx events, call this to turn off the debugger if you previously enabled it.
##### Example
\`\`\`js
htmx.logNone();
\`\`\`
### Property - `htmx.logger` {#logger}
The logger htmx uses to log with
##### Value
* `func(elt, eventName, detail)` - a function that takes an element, eventName and event detail and logs it
##### Example
\`\`\`js
htmx.logger = function(elt, event, data) {
if(console) {
console.log("INFO:", event, elt, data);
}
}
\`\`\`
### Method - `htmx.off()` {#off}
Removes an event listener from an element
##### Parameters
* `eventName` - the event name to remove the listener from
* `listener` - the listener to remove
or
* `target` - the element to remove the listener from
* `eventName` - the event name to remove the listener from
* `listener` - the listener to remove
##### Example
\`\`\`js
// remove this click listener from the body
htmx.off("click", myEventListener);
// remove this click listener from the given div
htmx.off("#my-div", "click", myEventListener)
\`\`\`
### Method - `htmx.on()` {#on}
Adds an event listener to an element
##### Parameters
* `eventName` - the event name to add the listener for
* `listener` - the listener to add
* `options` - an [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) object (or a [useCapture](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture) boolean) to add to the event listener (optional)
or
* `target` - the element to add the listener to
* `eventName` - the event name to add the listener for
* `listener` - the listener to add
* `options` - an [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) object (or a [useCapture](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture) boolean) to add to the event listener (optional)
##### Example
\`\`\`js
// add a click listener to the body
var myEventListener = htmx.on("click", function(evt){ console.log(evt); });
// add a click listener to the given div
var myEventListener = htmx.on("#my-div", "click", function(evt){ console.log(evt); });
// add a click listener to the given div that should only be invoked once
var myEventListener = htmx.on("#my-div", "click", function(evt){ console.log(evt); }, { once: true });
\`\`\`
### Method - `htmx.onLoad()` {#onLoad}
Adds a callback for the `htmx:load` event. This can be used to process new content, for example
initializing the content with a javascript library
##### Parameters
* `callback(elt)` - the callback to call on newly loaded content
##### Example
\`\`\`js
htmx.onLoad(function(elt){
MyLibrary.init(elt);
})
\`\`\`
### Method - `htmx.parseInterval()` {#parseInterval}
Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes.
Caution: Accepts an int followed by either `s` or `ms`. All other values use `parseFloat`
##### Parameters
* `str` - timing string
##### Example
\`\`\`js
// returns 3000
var milliseconds = htmx.parseInterval("3s");
// returns 3 - Caution
var milliseconds = htmx.parseInterval("3m");
\`\`\`
### Method - `htmx.process()` {#process}
Processes new content, enabling htmx behavior. This can be useful if you have content that is added to the DOM
outside of the normal htmx request cycle but still want htmx attributes to work.
##### Parameters
* `elt` - element to process
##### Example
\`\`\`js
document.body.innerHTML = "<div hx-get='/example'>Get it!</div>"
// process the newly added content
htmx.process(document.body);
\`\`\`
### Method - `htmx.remove()` {#remove}
Removes an element from the DOM
##### Parameters
* `elt` - element to remove
or
* `elt` - element to remove
* `delay` - delay (in milliseconds ) before element is removed
##### Example
\`\`\`js
// removes my-div from the DOM
htmx.remove(htmx.find("#my-div"));
// removes my-div from the DOM after a delay of 2 seconds
htmx.remove(htmx.find("#my-div"), 2000);
\`\`\`
### Method - `htmx.removeClass()` {#removeClass}
Removes a class from the given element
##### Parameters
* `elt` - element to remove the class from
* `class` - the class to remove
or
* `elt` - element to remove the class from
* `class` - the class to remove
* `delay` - delay (in milliseconds ) before class is removed
##### Example
\`\`\`js
// removes .myClass from my-div
htmx.removeClass(htmx.find("#my-div"), "myClass");
// removes .myClass from my-div after 6 seconds
htmx.removeClass(htmx.find("#my-div"), "myClass", 6000);
\`\`\`
### Method - `htmx.removeExtension()` {#removeExtension}
Removes the given extension from htmx
##### Parameters
* `name` - the name of the extension to remove
##### Example
\`\`\`js
htmx.removeExtension("my-extension");
\`\`\`
### Method - `htmx.swap()` {#swap}
Performs swapping (and settling) of HTML content
##### Parameters
* `target` - the HTML element or string selector of swap target
* `content` - string representation of content to be swapped
* `swapSpec` - swapping specification, representing parameters from `hx-swap`
* `swapStyle` (required) - swapping style (`innerHTML`, `outerHTML`, `beforebegin` etc)
* `swapDelay`, `settleDelay` (number) - delays before swapping and settling respectively
* `transition` (bool) - whether to use HTML transitions for swap
* `ignoreTitle` (bool) - disables page title updates
* `head` (string) - specifies `head` tag handling strategy (`merge` or `append`). Leave empty to disable head handling
* `scroll`, `scrollTarget`, `show`, `showTarget`, `focusScroll` - specifies scroll handling after swap
* `swapOptions` - additional *optional* parameters for swapping
* `select` - selector for the content to be swapped (equivalent of `hx-select`)
* `selectOOB` - selector for the content to be swapped out-of-band (equivalent of `hx-select-oob`)
* `eventInfo` - an object to be attached to `htmx:afterSwap` and `htmx:afterSettle` elements
* `anchor` - an anchor element that triggered scroll, will be scrolled into view on settle. Provides simple alternative to full scroll handling
* `contextElement` - DOM element that serves as context to swapping operation. Currently used to find extensions enabled for specific element
* `afterSwapCallback`, `afterSettleCallback` - callback functions called after swap and settle respectively. Take no arguments
##### Example
\`\`\`js
// swap #output element inner HTML with div element with "Swapped!" text
htmx.swap("#output", "<div>Swapped!</div>", {swapStyle: 'innerHTML'});
\`\`\`
### Method - `htmx.takeClass()` {#takeClass}
Takes the given class from its siblings, so that among its siblings, only the given element will have the class.
##### Parameters
* `elt` - the element that will take the class
* `class` - the class to take
##### Example
\`\`\`js
// takes the selected class from tab2's siblings
htmx.takeClass(htmx.find("#tab2"), "selected");
\`\`\`
### Method - `htmx.toggleClass()` {#toggleClass}
Toggles the given class on an element
##### Parameters
* `elt` - the element to toggle the class on
* `class` - the class to toggle
##### Example
\`\`\`js
// toggles the selected class on tab2
htmx.toggleClass(htmx.find("#tab2"), "selected");
\`\`\`
### Method - `htmx.trigger()` {#trigger}
Triggers a given event on an element
##### Parameters
* `elt` - the element to trigger the event on
* `name` - the name of the event to trigger
* `detail` - details for the event
##### Example
\`\`\`js
// triggers the myEvent event on #tab2 with the answer 42
htmx.trigger("#tab2", "myEvent", {answer:42});
\`\`\`
### Method - `htmx.values()` {#values}
Returns the input values that would resolve for a given element via the htmx value resolution mechanism
##### Parameters
* `elt` - the element to resolve values on
* `request type` - the request type (e.g. `get` or `post`) non-GET's will include the enclosing form of the element.
Defaults to `post`
##### Example
\`\`\`js
// gets the values associated with this form
var values = htmx.values(htmx.find("#myForm"));
\`\`\`
```
# attributes/hx-boost.md
```md
+++
title = "hx-boost"
description = """\
The hx-boost attribute in htmx enables progressive enhancement by converting standard HTML anchors and forms into \
AJAX requests, maintaining graceful fallback for users without JavaScript while providing modern dynamic page \
updates for those with JavaScript enabled."""
+++
The `hx-boost` attribute allows you to "boost" normal anchors and form tags to use AJAX instead. This
has the [nice fallback](https://en.wikipedia.org/wiki/Progressive_enhancement) that, if the user does not
have javascript enabled, the site will continue to work.
For anchor tags, clicking on the anchor will issue a `GET` request to the url specified in the `href` and
will push the url so that a history entry is created. The target is the `<body>` tag, and the `innerHTML`
swap strategy is used by default. All of these can be modified by using the appropriate attributes, except
the `click` trigger.
For forms the request will be converted into a `GET` or `POST`, based on the method in the `method` attribute
and will be triggered by a `submit`. Again, the target will be the `body` of the page, and the `innerHTML`
swap will be used. The url will _not_ be pushed, however, and no history entry will be created. (You can use the
[hx-push-url](@/attributes/hx-push-url.md) attribute if you want the url to be pushed.)
Here is an example of some boosted links:
\`\`\`html
<div hx-boost="true">
<a href="/page1">Go To Page 1</a>
<a href="/page2">Go To Page 2</a>
</div>
\`\`\`
These links will issue an ajax `GET` request to the respective URLs and replace the body's inner content with it.
Here is an example of a boosted form:
\`\`\`html
<form hx-boost="true" action="/example" method="post">
<input name="email" type="email" placeholder="Enter email...">
<button>Submit</button>
</form>
\`\`\`
This form will issue an ajax `POST` to the given URL and replace the body's inner content with it.
## Notes
* `hx-boost` is inherited and can be placed on a parent element
* Only links that are to the same domain and that are not local anchors will be boosted
* All requests are done via AJAX, so keep that in mind when doing things like redirects
* To find out if the request results from a boosted anchor or form, look for [`HX-Boosted`](@/reference.md#request_headers) in the request header
* Selectively disable boost on child elements with `hx-boost="false"`
* Disable the replacement of elements via boost, and their children, with [`hx-preserve="true"`](@/attributes/hx-preserve.md)
```
# attributes/hx-confirm.md
```md
+++
title = "hx-confirm"
description = """\
The hx-confirm attribute in htmx provides a way to add confirmation dialogs before executing requests, allowing \
you to protect users from accidental destructive actions. This documentation explains how to implement confirmation \
prompts and customize their behavior through event handling."""
+++
The `hx-confirm` attribute allows you to confirm an action before issuing a request. This can be useful
in cases where the action is destructive and you want to ensure that the user really wants to do it.
Here is an example:
\`\`\`html
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
\`\`\`
## Event details
The event triggered by `hx-confirm` contains additional properties in its `detail`:
* triggeringEvent: the event that triggered the original request
* issueRequest(skipConfirmation=false): a callback which can be used to confirm the AJAX request
* question: the value of the `hx-confirm` attribute on the HTML element
## Notes
* `hx-confirm` is inherited and can be placed on a parent element
* `hx-confirm` uses the browser's `window.confirm` by default. You can customize this behavior as shown [in this example](@/examples/confirm.md).
* a boolean `skipConfirmation` can be passed to the `issueRequest` callback; if true (defaults to false), the `window.confirm` will not be called and the AJAX request is issued directly
```
# attributes/hx-delete.md
```md
+++
title = "hx-delete"
description = """\
The hx-delete attribute in htmx will cause an element to issue a DELETE request to the specified URL and swap the \
returned HTML into the DOM using a swap strategy."""
+++
The `hx-delete` attribute will cause an element to issue a `DELETE` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-delete="/account" hx-target="body">
Delete Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `DELETE` to `/account` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-delete` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
* To remove the element following a successful `DELETE`, return a `200` status code with an empty body; if the server responds with a `204`, no swap takes place, documented here: [Requests & Responses](@/docs.md#requests)
```
# attributes/hx-disable.md
```md
+++
title = "hx-disable"
description = "The hx-disable attribute in htmx will disable htmx processing for a given element and all its children."
+++
The `hx-disable` attribute will disable htmx processing for a given element and all its children. This can be
useful as a backup for HTML escaping, when you include user generated content in your site, and you want to
prevent malicious scripting attacks.
The value of the tag is ignored, and it cannot be reversed by any content beneath it.
## Notes
* `hx-disable` is inherited
```
# attributes/hx-disabled-elt.md
```md
+++
title = "hx-disabled-elt"
description = """\
The hx-disabled-elt attribute in htmx allows you to specify elements that will have the `disabled` attribute added \
to them for the duration of the request."""
+++
The `hx-disabled-elt` attribute allows you to specify elements that will have the `disabled` attribute
added to them for the duration of the request. The value of this attribute can be:
* A CSS query selector of the element to disable.
* `this` to disable the element itself
* `closest <CSS selector>` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector
(e.g. `closest fieldset` will disable the closest to the element `fieldset`).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector
* `next` which resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling)
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector
(e.g. `next button` will disable the closest following sibling `button` element)
* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous input` will disable the closest previous sibling `input` element)
Here is an example with a button that will disable itself during a request:
\`\`\`html
<button hx-post="/example" hx-disabled-elt="this">
Post It!
</button>
\`\`\`
When a request is in flight, this will cause the button to be marked with [the `disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled),
which will prevent further clicks from occurring.
The `hx-disabled-elt` attribute also supports specifying multiple CSS selectors separated by commas to disable multiple elements during the request. Here is an example that disables buttons and text input fields of a particular form during the request:
\`\`\`html
<form hx-post="/example" hx-disabled-elt="find input[type='text'], find button">
<input type="text" placeholder="Type here...">
<button type="submit">Send</button>
</form>
\`\`\`
## Notes
* `hx-disabled-elt` is inherited and can be placed on a parent element
[hx-trigger]: https://htmx.org/attributes/hx-trigger/
```
# attributes/hx-disinherit.md
```md
+++
title = "hx-disinherit"
description = """\
The hx-disinherit attribute in htmx lets you control how child elements inherit attributes from their parents. This \
documentation explains how to selectively disable inheritance of specific htmx attributes or all attributes, \
allowing for more granular control over your web application's behavior."""
+++
The default behavior for htmx is to "inherit" many attributes automatically: that is, an attribute such as
[hx-target](@/attributes/hx-target.md) may be placed on a parent element, and all child elements will inherit
that target.
The `hx-disinherit` attribute allows you to control this automatic attribute inheritance. An example scenario is to
allow you to place an `hx-boost` on the `body` element of a page, but overriding that behavior in a specific part
of the page to allow for more specific behaviors.
htmx evaluates attribute inheritance as follows:
* when `hx-disinherit` is set on a parent node
* `hx-disinherit="*"` all attribute inheritance for this element will be disabled
* `hx-disinherit="hx-select hx-get hx-target"` disable inheritance for only one or multiple specified attributes
\`\`\`html
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-disinherit="*">
<a href="/page1">Go To Page 1</a> <!-- boosted with the attribute settings above -->
<a href="/page2" hx-boost="unset">Go To Page 1</a> <!-- not boosted -->
<button hx-get="/test" hx-target="this"></button> <!-- hx-select is not inherited -->
</div>
\`\`\`
\`\`\`html
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-disinherit="hx-target">
<!-- hx-select is automatically set to parent's value; hx-target is not inherited -->
<button hx-get="/test"></button>
</div>
\`\`\`
\`\`\`html
<div hx-select="#content">
<div hx-boost="true" hx-target="#content" hx-disinherit="hx-select">
<!-- hx-target is automatically inherited from parent's value -->
<!-- hx-select is not inherited, because the direct parent does
disables inheritance, despite not specifying hx-select itself -->
<button hx-get="/test"></button>
</div>
</div>
\`\`\`
## Notes
* Read more about [Attribute Inheritance](@/docs.md#inheritance)
```
# attributes/hx-encoding.md
```md
+++
title = "hx-encoding"
description = """\
The hx-encoding attribute in htmx allows you to switch the request encoding from the usual \
`application/x-www-form-urlencoded` encoding to `multipart/form-data`, usually to support file uploads in an AJAX \
request."""
+++
The `hx-encoding` attribute allows you to switch the request encoding from the usual `application/x-www-form-urlencoded`
encoding to `multipart/form-data`, usually to support file uploads in an ajax request.
The value of this attribute should be `multipart/form-data`.
The `hx-encoding` tag may be placed on parent elements.
## Notes
* `hx-encoding` is inherited and can be placed on a parent element
```
# attributes/hx-ext.md
```md
+++
title = "hx-ext"
description = """\
The hx-ext attribute in htmx enables one or more htmx extensions for an element and all its children. You can also \
use this attribute to ignore an extension that is enabled by a parent element."""
+++
The `hx-ext` attribute enables an htmx [extension](https://htmx.org/extensions) for an element and all its children.
The value can be a single extension name or a comma-separated list of extensions to apply.
The `hx-ext` tag may be placed on parent elements if you want a plugin to apply to an entire swath of the DOM,
and on the `body` tag for it to apply to all htmx requests.
## Notes
* `hx-ext` is both inherited and merged with parent elements, so you can specify extensions on any element in the DOM
hierarchy and it will apply to all child elements.
* You can ignore an extension that is defined by a parent node using `hx-ext="ignore:extensionName"`
\`\`\`html
<div hx-ext="example">
"Example" extension is used in this part of the tree...
<div hx-ext="ignore:example">
... but it will not be used in this part.
</div>
</div>
\`\`\`
\`\`\`html
<body hx-ext="preload,morph">
"preload" and "morph" extensions are used in this part of the tree...
</body>
\`\`\`
```
# attributes/hx-get.md
```md
+++
title = "hx-get"
description = """\
The hx-get attribute in htmx will cause an element to issue a GET request to the specified URL and swap the returned \
HTML into the DOM using a swap strategy."""
+++
The `hx-get` attribute will cause an element to issue a `GET` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-get="/example">Get Some HTML</button>
\`\`\`
This example will cause the `button` to issue a `GET` to `/example` and swap the returned HTML into
the `innerHTML` of the `button`.
### Notes
* `hx-get` is not inherited
* By default `hx-get` usually does not include any parameters. You can use the [hx-params](@/attributes/hx-params.md)
attribute to change this
* NB: If the element with the `hx-get` attribute also has a value, this will be included as a parameter unless explicitly removed
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
* An empty `hx-get:""` will make a get request to the current url and will swap the current HTML page
```
# attributes/hx-headers.md
```md
+++
title = "hx-headers"
description = """\
The hx-headers attribute in htmx allows you to add to the headers that will be submitted with an AJAX request."""
+++
The `hx-headers` attribute allows you to add to the headers that will be submitted with an AJAX request.
By default, the value of this attribute is a list of name-expression values in [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html)
format.
If you wish for `hx-headers` to *evaluate* the values given, you can prefix the values with `javascript:` or `js:`.
\`\`\`html
<div hx-get="/example" hx-headers='{"myHeader": "My Value"}'>Get Some HTML, Including A Custom Header in the Request</div>
<div hx-get="/example" hx-headers='js:{myVal: calculateValue()}'>Get Some HTML, Including a Dynamic Custom Header from Javascript in the Request</div>
\`\`\`
## Security Considerations
* By default, the value of `hx-headers` must be valid [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON).
It is **not** dynamically computed. If you use the `javascript:` prefix, be aware that you are introducing
security considerations, especially when dealing with user input such as query strings or user-generated content,
which could introduce a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability.
* Whilst far from being a foolproof solution to [Cross-Site Request Forgery](https://owasp.org/www-community/attacks/csrf), the `hx-headers` attribute can support backend services to provide [CSRF prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html). For more information see the [CSRF Prevention](https://htmx.org/docs/#csrf-prevention) section.
## Notes
* `hx-headers` is inherited and can be placed on a parent element.
* A child declaration of a header overrides a parent declaration.
```
# attributes/hx-history-elt.md
```md
+++
title = "hx-history-elt"
description = """\
The hx-history-elt attribute in htmx allows you to specify the element that will be used to snapshot and restore \
page state during navigation. In most cases we do not recommend using this element."""
+++
The `hx-history-elt` attribute allows you to specify the element that will be used to snapshot and
restore page state during navigation. By default, the `body` tag is used. This is typically
good enough for most setups, but you may want to narrow it down to a child element. Just make
sure that the element is always visible in your application, or htmx will not be able to restore
history navigation properly.
Here is an example:
\`\`\`html
<html>
<body>
<div id="content" hx-history-elt>
...
</div>
</body>
</html>
\`\`\`
## Notes
* `hx-history-elt` is not inherited
* In most cases we don't recommend narrowing the history snapshot
```
# attributes/hx-history.md
```md
+++
title = "hx-history"
description = """\
The hx-history attribute in htmx allows you to prevent sensitive page data from being stored in the browser's \
localStorage cache during history navigation, ensuring that the page state is retrieved from the server instead when \
navigating through history."""
+++
Set the `hx-history` attribute to `false` on any element in the current document, or any html fragment loaded into the current document by htmx, to prevent sensitive data being saved to the localStorage cache when htmx takes a snapshot of the page state.
History navigation will work as expected, but on restoration the URL will be requested from the server instead of the history cache.
Here is an example:
\`\`\`html
<html>
<body>
<div hx-history="false">
...
</div>
</body>
</html>
\`\`\`
## Notes
* `hx-history="false"` can be present *anywhere* in the document to embargo the current page state from the history cache (i.e. even outside the element specified for the history snapshot [hx-history-elt](@/attributes/hx-history-elt.md)).
```
# attributes/hx-include.md
```md
+++
title = "hx-include"
description = "The hx-include attribute in htmx allows you to include additional element values in an AJAX request."
+++
The `hx-include` attribute allows you to include additional element values in an AJAX request. The value of this
attribute can be:
* A CSS query selector of the elements to include.
* `this` which will include the descendants of the element.
* `closest <CSS selector>` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous .error` will target the closest previous sibling with `error` class)
Here is an example that includes a separate input value:
\`\`\`html
<div>
<button hx-post="/register" hx-include="[name='email']">
Register!
</button>
Enter email: <input name="email" type="email"/>
</div>
\`\`\`
This is a little contrived as you would typically enclose both of these elements in a `form` and submit
the value automatically, but it demonstrates the concept.
Note that if you include a non-input element, all input elements enclosed in that element will be included.
## Notes
* `hx-include` is inherited and can be placed on a parent element
* While `hx-include` is inherited, it is evaluated from the element triggering the request. It is easy to get confused
when working with the extended selectors such as `find` and `closest`.
\`\`\`html
<div hx-include="find input">
<button hx-post="/register">
Register!
</button>
Enter email: <input name="email" type="email"/>
</div>
\`\`\`
In the above example, when clicking on the button, the `find input` selector is resolved from the button itself, which
does not return any element here, since the button doesn't have any `input` child, thus in this case, raises an error.
* A standard CSS selector resolves
to [document.querySelectorAll](https://developer.mozilla.org/docs/Web/API/Document/querySelectorAll) and will include
multiple elements, while the extended selectors such as `find` or `next` only return a single element at most to
include
* `hx-include` will ignore disabled inputs
```
# attributes/hx-indicator.md
```md
+++
title = "hx-indicator"
description = """\
The hx-indicator attribute in htmx allows you to specify the element that will have the `htmx-request` class added \
to it for the duration of the request. This can be used to show spinners or progress indicators while the request is \
in flight."""
+++
The `hx-indicator` attribute allows you to specify the element that will have the `htmx-request` class
added to it for the duration of the request. This can be used to show spinners or progress indicators
while the request is in flight.
The value of this attribute is a CSS query selector of the element or elements to apply the class to,
or the keyword [`closest`](https://developer.mozilla.org/docs/Web/API/Element/closest), followed by a CSS selector,
which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. `closest tr`);
Here is an example with a spinner adjacent to the button:
\`\`\`html
<div>
<button hx-post="/example" hx-indicator="#spinner">
Post It!
</button>
<img id="spinner" class="htmx-indicator" src="/img/bars.svg"/>
</div>
\`\`\`
When a request is in flight, this will cause the `htmx-request` class to be added to the `#spinner`
image. The image also has the `htmx-indicator` class on it, which defines an opacity transition
that will show the spinner:
\`\`\`css
.htmx-indicator{
opacity:0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator{
opacity:1;
}
.htmx-request.htmx-indicator{
opacity:1;
}
\`\`\`
If you would prefer a different effect for showing the spinner you could define and use your own indicator
CSS. Here is an example that uses `display` rather than opacity (Note that we use `my-indicator` instead of `htmx-indicator`):
\`\`\`css
.my-indicator{
display:none;
}
.htmx-request .my-indicator{
display:inline;
}
.htmx-request.my-indicator{
display:inline;
}
\`\`\`
Note that the target of the `hx-indicator` selector need not be the exact element that you
want to show: it can be any element in the parent hierarchy of the indicator.
Finally, note that the `htmx-request` class by default is added to the element causing
the request, so you can place an indicator inside of that element and not need to explicitly
call it out with the `hx-indicator` attribute:
\`\`\`html
<button hx-post="/example">
Post It!
<img class="htmx-indicator" src="/img/bars.svg"/>
</button>
\`\`\`
## Demo
This simulates what a spinner might look like in that situation:
<button class="btn" classes="toggle htmx-request:3s">
Post It!
<img class="htmx-indicator" src="/img/bars.svg"/>
</button>
## Notes
* `hx-indicator` is inherited and can be placed on a parent element
* In the absence of an explicit indicator, the `htmx-request` class will be added to the element triggering the
request
* If you want to use your own CSS but still use `htmx-indicator` as class name, then you need to disable `includeIndicatorStyles`. See [Configuring htmx](@/docs.md#config). The easiest way is to add this to the `<head>` of your HTML:
\`\`\`html
<meta name="htmx-config" content='{"includeIndicatorStyles": false}'>
\`\`\`
```
# attributes/hx-inherit.md
```md
+++
title = "hx-inherit"
description = """\
The hx-inherit attribute in htmx allows you to explicitly control attribute inheritance behavior between parent and \
child elements, providing fine-grained control over which htmx attributes are inherited when the default inheritance \
system is disabled through configuration."""
+++
The default behavior for htmx is to "inherit" many attributes automatically: that is, an attribute such as
[hx-target](@/attributes/hx-target.md) may be placed on a parent element, and all child elements will inherit
that target. Some people do not like this feature and instead prefer to explicitly specify inheritance for attributes.
To support this mode of development, htmx offers the `htmx.config.disableInheritance` setting, which can be set to
`true` to prevent inheritance from being the default behavior for any of the htmx attributes.
The `hx-inherit` attribute allows you to control the inheritance of attributes manually.
htmx evaluates attribute inheritance as follows:
* when `hx-inherit` is set on a parent node
* `inherit="*"` all attribute inheritance for this element will be enabled
* `hx-inherit="hx-select hx-get hx-target"` enable inheritance for only one or multiple specified attributes
Here is an example of a div that shares an `hx-target` attribute for a set of anchor tags when `htmx.config.disableInheritance`
is set to false:
\`\`\`html
<div hx-target="#tab-container" hx-inherit="hx-target">
<a hx-boost="true" href="/tab1">Tab 1</a>
<a hx-boost="true" href="/tab2">Tab 2</a>
<a hx-boost="true" href="/tab3">Tab 3</a>
</div>
\`\`\`
## Notes
* Read more about [Attribute Inheritance](@/docs.md#inheritance)
```
# attributes/hx-on.md
```md
+++
title = "hx-on"
description = """\
The hx-on attributes in htmx allow you to write inline JavaScript event handlers directly on HTML elements, \
supporting both standard DOM events and htmx-specific events with improved locality of behavior."""
+++
The `hx-on*` attributes allow you to embed scripts inline to respond to events directly on an element; similar to the
[`onevent` properties](https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers#using_onevent_properties) found in HTML, such as `onClick`.
The `hx-on*` attributes improve upon `onevent` by enabling the handling of any arbitrary JavaScript event,
for enhanced [Locality of Behaviour (LoB)](/essays/locality-of-behaviour/) even when dealing with non-standard DOM events. For example, these
attributes allow you to handle [htmx events](/reference#events).
With `hx-on` attributes, you specify the event name as part of the attribute name, after a colon. So, for example, if
you want to respond to a `click` event, you would use the attribute `hx-on:click`:
\`\`\`html
<div hx-on:click="alert('Clicked!')">Click</div>
\`\`\`
Note that this syntax can be used to capture all htmx events, as well as most other custom events, in addition to the
standard DOM events.
One gotcha to note is that DOM attributes do not preserve case. This means, unfortunately, an attribute like
`hx-on:htmx:beforeRequest` **will not work**, because the DOM lowercases the attribute names. Fortunately, htmx supports
both camel case event names and also [kebab-case event names](@/docs.md#events), so you can use `hx-on:htmx:before-request` instead.
In order to make writing htmx-based event handlers a little easier, you can use the shorthand double-colon `hx-on::` for htmx
events, and omit the "htmx" part:
\`\`\`html
<!-- These two are equivalent -->
<button hx-get="/info" hx-on:htmx:before-request="alert('Making a request!')">
Get Info!
</button>
<button hx-get="/info" hx-on::before-request="alert('Making a request!')">
Get Info!
</button>
\`\`\`
If you wish to handle multiple different events, you can simply add multiple attributes to an element:
\`\`\`html
<button hx-get="/info"
hx-on::before-request="alert('Making a request!')"
hx-on::after-request="alert('Done making a request!')">
Get Info!
</button>
\`\`\`
Finally, in order to make this feature compatible with some templating languages (e.g. [JSX](https://react.dev/learn/writing-markup-with-jsx)) that do not like having a colon (`:`)
in HTML attributes, you may use dashes in the place of colons for both the long form and the shorthand form:
\`\`\`html
<!-- These two are equivalent -->
<button hx-get="/info" hx-on-htmx-before-request="alert('Making a request!')">
Get Info!
</button>
<button hx-get="/info" hx-on--before-request="alert('Making a request!')">
Get Info!
</button>
\`\`\`
### hx-on (deprecated)
The value is an event name, followed by a colon `:`, followed by the script:
\`\`\`html
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')">
Get Info!
</button>
\`\`\`
Multiple handlers can be defined by putting them on new lines:
\`\`\`html
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')
htmx:afterRequest: alert('Done making a request!')">
Get Info!
</button>
\`\`\`
### Symbols
Like `onevent`, two symbols are made available to event handler scripts:
* `this` - The element on which the `hx-on` attribute is defined
* `event` - The event that triggered the handler
### Notes
* `hx-on` is _not_ inherited, however due to
[event bubbling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling_and_capture),
`hx-on` attributes on parent elements will typically be triggered by events on child elements
* `hx-on:*` and `hx-on` cannot be used together on the same element; if `hx-on:*` is present, the value of an `hx-on` attribute
on the same element will be ignored. The two forms can be mixed in the same document, however.
```
# attributes/hx-params.md
```md
+++
title = "hx-params"
description = """\
The hx-params attribute in htmx allows you to filter the parameters that will be submitted with an AJAX request."""
+++
The `hx-params` attribute allows you to filter the parameters that will be submitted with an AJAX request.
The possible values of this attribute are:
* `*` - Include all parameters (default)
* `none` - Include no parameters
* `not <param-list>` - Include all except the comma separated list of parameter names
* `<param-list>` - Include all the comma separated list of parameter names
\`\`\`html
<div hx-get="/example" hx-params="*">Get Some HTML, Including Params</div>
\`\`\`
This div will include all the parameters that a `POST` would, but they will be URL encoded
and included in the URL, as per usual with a `GET`.
## Notes
* `hx-params` is inherited and can be placed on a parent element
```
# attributes/hx-patch.md
```md
+++
title = "hx-patch"
description = """\
The hx-patch attribute in htmx will cause an element to issue a PATCH request to the specified URL and swap the \
returned HTML into the DOM using a swap strategy."""
+++
The `hx-patch` attribute will cause an element to issue a `PATCH` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-patch="/account" hx-target="body">
Patch Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `PATCH` to `/account` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-patch` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
```
# attributes/hx-post.md
```md
+++
title = "hx-post"
description = """\
The hx-post attribute in htmx will cause an element to issue a POST request to the specified URL and swap the \
returned HTML into the DOM using a swap strategy."""
+++
The `hx-post` attribute will cause an element to issue a `POST` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-post="/account/enable" hx-target="body">
Enable Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `POST` to `/account/enable` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-post` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
```
# attributes/hx-preserve.md
```md
+++
title = "hx-preserve"
description = """\
The hx-preserve attribute in htmx allows you to keep an element unchanged during HTML replacement. Elements with \
hx-preserve set are preserved by `id` when htmx updates any ancestor element."""
+++
The `hx-preserve` attribute allows you to keep an element unchanged during HTML replacement.
Elements with `hx-preserve` set are preserved by `id` when htmx updates any ancestor element.
You *must* set an unchanging `id` on elements for `hx-preserve` to work.
The response requires an element with the same `id`, but its type and other attributes are ignored.
## Notes
* `hx-preserve` is not inherited
* You can use `hx-preserve="true"` or use it as a boolean attribute with just `hx-preserve`
* Some elements cannot unfortunately be preserved properly, such as `<input type="text">` (focus and caret position are lost), iframes or certain types of videos. To tackle some of these cases we recommend the [morphdom extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md), which does a more elaborate DOM
reconciliation
* When using [History Support](@/docs.md#history) for actions like the back button `hx-preserve` elements will also have their state preserved
* Avoid using [hx-swap](@/attributes/hx-swap.md) set to `none` with requests that could contain a `hx-preserve` element to avoid losing it
* `hx-preserve` can cause elements to be removed from their current location and relocated to a new location when swapping in a partial/oob response
\`\`\`html
<div id="new_location">
Just relocated the video here
<div id="video" hx-preserve></div>
</div>
\`\`\`
* Can be used on the inside content of a [hx-swap-oob](@/attributes/hx-swap-oob.md) element
\`\`\`html
<div id="notify" hx-swap-oob="true">
Notification updated but keep the same retain
<div id="retain" hx-preserve></div>
</div>
\`\`\`
```
# attributes/hx-prompt.md
```md
+++
title = "hx-prompt"
description = """\
The hx-prompt attribute in htmx allows you to show a prompt before issuing a request. The value of the prompt will \
be included in the request in the `HX-Prompt` header."""
+++
The `hx-prompt` attribute allows you to show a prompt before issuing a request. The value of
the prompt will be included in the request in the `HX-Prompt` header.
Here is an example:
\`\`\`html
<button hx-delete="/account" hx-prompt="Enter your account name to confirm deletion">
Delete My Account
</button>
\`\`\`
## Notes
* `hx-prompt` is inherited and can be placed on a parent element
```
# attributes/hx-push-url.md
```md
+++
title = "hx-push-url"
description = """\
The hx-push-url attribute in htmx allows you to push a URL into the browser location history. This creates a new \
history entry, allowing navigation with the browser's back and forward buttons."""
+++
The `hx-push-url` attribute allows you to push a URL into the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
This creates a new history entry, allowing navigation with the browser’s back and forward buttons.
htmx snapshots the current DOM and saves it into its history cache, and restores from this cache on navigation.
The possible values of this attribute are:
1. `true`, which pushes the fetched URL into history.
2. `false`, which disables pushing the fetched URL if it would otherwise be pushed due to inheritance or [`hx-boost`](/attributes/hx-boost).
3. A URL to be pushed into the location bar.
This may be relative or absolute, as per [`history.pushState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState).
Here is an example:
\`\`\`html
<div hx-get="/account" hx-push-url="true">
Go to My Account
</div>
\`\`\`
This will cause htmx to snapshot the current DOM to `localStorage` and push the URL `/account' into the browser location bar.
Another example:
\`\`\`html
<div hx-get="/account" hx-push-url="/account/home">
Go to My Account
</div>
\`\`\`
This will push the URL `/account/home' into the location history.
## Notes
* `hx-push-url` is inherited and can be placed on a parent element
* The [`HX-Push-Url` response header](@/headers/hx-push-url.md) has similar behavior and can override this attribute.
* The [`hx-history-elt` attribute](@/attributes/hx-history-elt.md) allows changing which element is saved in the history cache.
```
# attributes/hx-put.md
```md
+++
title = "hx-put"
description = """\
The hx-put attribute in htmx will cause an element to issue a PUT request to the specified URL and swap the returned \
HTML into the DOM using a swap strategy."""
+++
The `hx-put` attribute will cause an element to issue a `PUT` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-put="/account" hx-target="body">
Put Money In Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `PUT` to `/account` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-put` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
```
# attributes/hx-replace-url.md
```md
+++
title = "hx-replace-url"
description = """\
The hx-replace-url attribute in htmx allows you to replace the current URL of the browser location history."""
+++
The `hx-replace-url` attribute allows you to replace the current url of the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
The possible values of this attribute are:
1. `true`, which replaces the fetched URL in the browser navigation bar.
2. `false`, which disables replacing the fetched URL if it would otherwise be replaced due to inheritance.
3. A URL to be replaced into the location bar.
This may be relative or absolute, as per [`history.replaceState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState).
Here is an example:
\`\`\`html
<div hx-get="/account" hx-replace-url="true">
Go to My Account
</div>
\`\`\`
This will cause htmx to snapshot the current DOM to `localStorage` and replace the URL `/account' in the browser location bar.
Another example:
\`\`\`html
<div hx-get="/account" hx-replace-url="/account/home">
Go to My Account
</div>
\`\`\`
This will replace the URL `/account/home' in the browser location bar.
## Notes
* `hx-replace-url` is inherited and can be placed on a parent element
* The [`HX-Replace-Url` response header](@/headers/hx-replace-url.md) has similar behavior and can override this attribute.
* The [`hx-history-elt` attribute](@/attributes/hx-history-elt.md) allows changing which element is saved in the history cache.
* The [`hx-push-url` attribute](@/attributes/hx-push-url.md) is a similar and more commonly used attribute, which creates a
new history entry rather than replacing the current one.
```
# attributes/hx-request.md
```md
+++
title = "hx-request"
description = """\
The hx-request attribute in htmx allows you to configure the request timeout, whether the request will send \
credentials, and whether the request will include headers."""
+++
The `hx-request` attribute allows you to configure various aspects of the request via the following attributes:
* `timeout` - the timeout for the request, in milliseconds
* `credentials` - if the request will send credentials
* `noHeaders` - strips all headers from the request
These attributes are set using a JSON-like syntax:
\`\`\`html
<div ... hx-request='{"timeout":100}'>
...
</div>
\`\`\`
You may make the values dynamically evaluated by adding the `javascript:` or `js:` prefix:
\`\`\`html
<div ... hx-request='js: timeout:getTimeoutSetting() '>
...
</div>
\`\`\`
## Notes
* `hx-request` is merge-inherited and can be placed on a parent element
```
# attributes/hx-select-oob.md
```md
+++
title = "hx-select-oob"
description = """\
The hx-select-oob attribute in htmx allows you to select content from a response to be swapped in via an out-of-band \
swap. The value of this attribute is comma separated list of elements to be swapped out of band."""
+++
The `hx-select-oob` attribute allows you to select content from a response to be swapped in via an out-of-band swap.
The value of this attribute is comma separated list of elements to be swapped out of band. This attribute is almost
always paired with [hx-select](@/attributes/hx-select.md).
Here is an example that selects a subset of the response content:
\`\`\`html
<div>
<div id="alert"></div>
<button hx-get="/info"
hx-select="#info-details"
hx-swap="outerHTML"
hx-select-oob="#alert">
Get Info!
</button>
</div>
\`\`\`
This button will issue a `GET` to `/info` and then select the element with the id `info-details`,
which will replace the entire button in the DOM, and, in addition, pick out an element with the id `alert`
in the response and swap it in for div in the DOM with the same ID.
Each value in the comma separated list of values can specify any valid [`hx-swap`](@/attributes/hx-swap.md)
strategy by separating the selector and the swap strategy with a `:`, with the strategy otherwise defaulting to `outerHTML`.
For example, to prepend the alert content instead of replacing it:
\`\`\`html
<div>
<div id="alert"></div>
<button hx-get="/info"
hx-select="#info-details"
hx-swap="outerHTML"
hx-select-oob="#alert:afterbegin">
Get Info!
</button>
</div>
\`\`\`
## Notes
* `hx-select-oob` is inherited and can be placed on a parent element
```
# attributes/hx-select.md
```md
+++
title = "hx-select"
description = "The hx-select attribute in htmx allows you to select the content you want swapped from a response."
+++
The `hx-select` attribute allows you to select the content you want swapped from a response. The value of
this attribute is a CSS query selector of the element or elements to select from the response.
Here is an example that selects a subset of the response content:
\`\`\`html
<div>
<button hx-get="/info" hx-select="#info-detail" hx-swap="outerHTML">
Get Info!
</button>
</div>
\`\`\`
So this button will issue a `GET` to `/info` and then select the element with the id `info-detail`,
which will replace the entire button in the DOM.
## Notes
* `hx-select` is inherited and can be placed on a parent element
```
# attributes/hx-swap-oob.md
```md
+++
title = "hx-swap-oob"
description = """\
The hx-swap-oob attribute in htmx allows you to specify that some content in a response should be swapped into the \
DOM somewhere other than the target, that is 'out-of-band'. This allows you to piggyback updates to other elements \
on a response."""
+++
The `hx-swap-oob` attribute allows you to specify that some content in a response should be
swapped into the DOM somewhere other than the target, that is "Out of Band". This allows you to piggyback updates to other element updates on a response.
Consider the following response HTML:
\`\`\`html
<div>
...
</div>
<div id="alerts" hx-swap-oob="true">
Saved!
</div>
\`\`\`
The first div will be swapped into the target the usual manner. The second div, however, will be swapped in as a replacement for the element with the id `alerts`, and will not end up in the target.
The value of the `hx-swap-oob` can be:
* `true`
* any valid [`hx-swap`](@/attributes/hx-swap.md) value
* any valid [`hx-swap`](@/attributes/hx-swap.md) value, followed by a colon, followed by a CSS selector
If the value is `true` or `outerHTML` (which are equivalent) the element will be swapped inline.
If a swap value is given, that swap strategy will be used and the encapsulating tag pair will be stripped for all strategies other than `outerHTML`.
If a selector is given, all elements matched by that selector will be swapped. If not, the element with an ID matching the new content will be swapped.
### Using alternate swap strategies
As mentioned previously when using swap strategies other than `true` or `outerHTML` the encapsulating tags are stripped, as such you need to excapsulate the returned data with the correct tags for the context.
When trying to insert a `<tr>` in a table that uses `<tbody>`:
\`\`\`html
<tbody hx-swap-oob="beforeend:#table tbody">
<tr>
...
</tr>
</tbody>
\`\`\`
A "plain" table:
\`\`\`html
<table hx-swap-oob="beforeend:#table2">
<tr>
...
</tr>
</table>
\`\`\`
A `<li>` may be encapsulated in `<ul>`, `<ol>`, `<div>` or `<span>`, for example:
\`\`\`html
<ul hx-swap-oob="beforeend:#list1">
<li>...</li>
</ul>
\`\`\`
A `<p>` can be encapsulated in `<div>` or `<span>`:
\`\`\`html
<span hx-swap-oob="beforeend:#text">
<p>...</p>
</span>
\`\`\`
### Troublesome Tables and lists
Note that you can use a `template` tag to encapsulate types of elements that, by the HTML spec, can't stand on their own in the
DOM (`<tr>`, `<td>`, `<th>`, `<thead>`, `<tbody>`, `<tfoot>`, `<colgroup>`, `<caption>`, `<col>` & `<li>`).
Here is an example with an out-of-band swap of a table row being encapsulated in this way:
\`\`\`html
<div>
...
</div>
<template>
<tr id="row" hx-swap-oob="true">
...
</tr>
</template>
\`\`\`
Note that these template tags will be removed from the final content of the page.
### Slippery SVGs
Some element types, like SVG, use a specific XML namespace for their child elements. This prevents internal elements from working correctly when swapped in, unless they are encapsulated within a `svg` tag. To modify the internal contents of an existing SVG, you can use both `template` and `svg` tags to encapsulate the elements, allowing them to get the correct namespace applied.
Here is an example with an out-of-band swap of svg elements being encapsulated in this way:
\`\`\`html
<div>
...
</div>
<template><svg>
<circle hx-swap-oob="true" id="circle1" r="35" cx="50" cy="50" fill="red" />
</svg></template>
<template><svg hx-swap-oob="beforebegin:#circle1">
<circle id="circle2" r="45" cx="50" cy="50" fill="blue" />
</svg></template>
\`\`\`
This will replace circle1 inline and then insert circle2 before circle1.
Note that these `template` and `svg` wrapping tags will be removed from the final content of the page.
## Nested OOB Swaps
By default, any element with `hx-swap-oob=` attribute anywhere in the response is processed for oob swap behavior, including when an element is nested within the main response element.
This can be problematic when using [template fragments](https://htmx.org/essays/template-fragments/) where a fragment may be reused as an oob-swap target and also as part of a bigger fragment. When the bigger fragment is the main response the inner fragment will still be processed as an oob swap, removing it from the dom.
This behavior can be changed by setting the config `htmx.config.allowNestedOobSwaps` to `false`. If this config option is `false`, OOB swaps are only processed when the element is *adjacent to* the main response element, OOB swaps elsewhere will be ignored and oob-swap-related attributes stripped.
## Notes
* `hx-swap-oob` is not inherited
```
# attributes/hx-swap.md
```md
+++
title = "hx-swap"
description = """\
The hx-swap attribute in htmx allows you to specify the 'swap strategy', or how the response will be swapped in \
relative to the target of an AJAX request. The default swap strategy is `innerHTML`."""
+++
The `hx-swap` attribute allows you to specify how the response will be swapped in relative to the
[target](@/attributes/hx-target.md) of an AJAX request. If you do not specify the option, the default is
`htmx.config.defaultSwapStyle` (`innerHTML`).
The possible values of this attribute are:
* `innerHTML` - Replace the inner html of the target element
* `outerHTML` - Replace the entire target element with the response
* `textContent` - Replace the text content of the target element, without parsing the response as HTML
* `beforebegin` - Insert the response before the target element
* `afterbegin` - Insert the response before the first child of the target element
* `beforeend` - Insert the response after the last child of the target element
* `afterend` - Insert the response after the target element
* `delete` - Deletes the target element regardless of the response
* `none`- Does not append content from response (out of band items will still be processed).
These options are based on standard DOM naming and the
[`Element.insertAdjacentHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML)
specification.
So in this code:
\`\`\`html
<div hx-get="/example" hx-swap="afterend">Get Some HTML & Append It</div>
\`\`\`
The `div` will issue a request to `/example` and append the returned content after the `div`
### Modifiers
The `hx-swap` attributes supports modifiers for changing the behavior of the swap. They are outlined below.
#### Transition: `transition`
If you want to use the new [View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API
when a swap occurs, you can use the `transition:true` option for your swap. You can also enable this feature globally by
setting the `htmx.config.globalViewTransitions` config setting to `true`.
#### Timing: `swap` & `settle`
You can modify the amount of time that htmx will wait after receiving a response to swap the content
by including a `swap` modifier:
\`\`\`html
<!-- this will wait 1s before doing the swap after it is received -->
<div hx-get="/example" hx-swap="innerHTML swap:1s">Get Some HTML & Append It</div>
\`\`\`
Similarly, you can modify the time between the swap and the settle logic by including a `settle`
modifier:
\`\`\`html
<!-- this will wait 1s before doing the settle after it is received -->
<div hx-get="/example" hx-swap="innerHTML settle:1s">Get Some HTML & Append It</div>
\`\`\`
These attributes can be used to synchronize htmx with the timing of CSS transition effects.
#### Title: `ignoreTitle`
By default, htmx will update the title of the page if it finds a `<title>` tag in the response content. You can turn
off this behavior by setting the `ignoreTitle` option to true.
#### Scrolling: `scroll` & `show`
You can also change the scrolling behavior of the target element by using the `scroll` and `show` modifiers, both
of which take the values `top` and `bottom`:
\`\`\`html
<!-- this fixed-height div will scroll to the bottom of the div after content is appended -->
<div style="height:200px; overflow: scroll"
hx-get="/example"
hx-swap="beforeend scroll:bottom">
Get Some HTML & Append It & Scroll To Bottom
</div>
\`\`\`
\`\`\`html
<!-- this will get some content and add it to #another-div, then ensure that the top of #another-div is visible in the
viewport -->
<div hx-get="/example"
hx-swap="innerHTML show:top"
hx-target="#another-div">
Get Some Content
</div>
\`\`\`
If you wish to target a different element for scrolling or showing, you may place a CSS selector after the `scroll:`
or `show:`, followed by `:top` or `:bottom`:
\`\`\`html
<!-- this will get some content and swap it into the current div, then ensure that the top of #another-div is visible in the
viewport -->
<div hx-get="/example"
hx-swap="innerHTML show:#another-div:top">
Get Some Content
</div>
\`\`\`
You may also use `window:top` and `window:bottom` to scroll to the top and bottom of the current window.
\`\`\`html
<!-- this will get some content and swap it into the current div, then ensure that the viewport is scrolled to the
very top -->
<div hx-get="/example"
hx-swap="innerHTML show:window:top">
Get Some Content
</div>
\`\`\`
For boosted links and forms the default behaviour is `show:top`. You can disable it globally with
[htmx.config.scrollIntoViewOnBoost](@/api.md#config) or you can use `hx-swap="show:none"` on an element basis.
\`\`\`html
<form action="/example" hx-swap="show:none">
...
</form>
\`\`\`
#### Focus scroll
htmx preserves focus between requests for inputs that have a defined id attribute. By default htmx prevents auto-scrolling to focused inputs between requests which can be unwanted behavior on longer requests when the user has already scrolled away. To enable focus scroll you can use `focus-scroll:true`.
\`\`\`html
<input id="name" hx-get="/validation"
hx-swap="outerHTML focus-scroll:true"/>
\`\`\`
Alternatively, if you want the page to automatically scroll to the focused element after each request you can change the htmx global configuration value `htmx.config.defaultFocusScroll` to true. Then disable it for specific requests using `focus-scroll:false`.
\`\`\`html
<input id="name" hx-get="/validation"
hx-swap="outerHTML focus-scroll:false"/>
\`\`\`
## Notes
* `hx-swap` is inherited and can be placed on a parent element
* The default value of this attribute is `innerHTML`
* Due to DOM limitations, it’s not possible to use the `outerHTML` method on the `<body>` element.
htmx will change `outerHTML` on `<body>` to use `innerHTML`.
* The default swap delay is 0ms
* The default settle delay is 20ms
```
# attributes/hx-sync.md
```md
+++
title = "hx-sync"
description = "The hx-sync attribute in htmx allows you to synchronize AJAX requests between multiple elements."
+++
The `hx-sync` attribute allows you to synchronize AJAX requests between multiple elements.
The `hx-sync` attribute consists of a CSS selector to indicate the element to synchronize on, followed optionally
by a colon and then by an optional syncing strategy. The available strategies are:
* `drop` - drop (ignore) this request if an existing request is in flight (the default)
* `abort` - drop (ignore) this request if an existing request is in flight, and, if that is not the case,
*abort* this request if another request occurs while it is still in flight
* `replace` - abort the current request, if any, and replace it with this request
* `queue` - place this request in the request queue associated with the given element
The `queue` modifier can take an additional argument indicating exactly how to queue:
* `queue first` - queue the first request to show up while a request is in flight
* `queue last` - queue the last request to show up while a request is in flight
* `queue all` - queue all requests that show up while a request is in flight
## Notes
* `hx-sync` is inherited and can be placed on a parent element
This example resolves a race condition between a form's submit request and an individual input's validation request. Normally, without using `hx-sync`, filling out the input and immediately submitting the form triggers two parallel requests to `/validate` and `/store`. Using `hx-sync="closest form:abort"` on the input will watch for requests on the form and abort the input's request if a form request is present or starts while the input request is in flight.
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
\`\`\`
If you'd rather prioritize the validation request over the submit request, you can use the `drop` strategy. This example will prioritize the validation request over the submit request so that if a validation request is in flight, the form cannot be submitted.
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:drop"
>
<button type="submit">Submit</button>
</form>
\`\`\`
When dealing with forms that contain many inputs, you can prioritize the submit request over all input validation requests using the hx-sync `replace` strategy on the form tag. This will cancel any in-flight validation requests and issue only the `hx-post="/store"` request. If you'd rather abort the submit request and prioritize any existing validation requests you can use the `hx-sync="this:abort"` strategy on the form tag.
\`\`\`html
<form hx-post="/store" hx-sync="this:replace">
<input id="title" name="title" type="text" hx-post="/validate" hx-trigger="change" />
<button type="submit">Submit</button>
</form>
\`\`\`
When implementing active search functionality the hx-trigger attribute's `delay` modifier can be used to debounce the user's input and avoid making multiple requests while the user types. However, once a request is made, if the user begins typing again a new request will begin even if the previous one has not finished processing. This example will cancel any in-flight requests and use only the last request. In cases where the search input is contained within the target, then using `hx-sync` like this also helps reduce the chances that the input will be replaced while the user is still typing.
\`\`\`html
<input type="search"
hx-get="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-sync="this:replace">
\`\`\`
```
# attributes/hx-target.md
```md
+++
title = "hx-target"
description = """\
The hx-target attribute in htmx allows you to target a different element for swapping than the one issuing the AJAX \
request."""
+++
The `hx-target` attribute allows you to target a different element for swapping than the one issuing the AJAX
request. The value of this attribute can be:
* A CSS query selector of the element to target.
* `this` which indicates that the element that the `hx-target` attribute is on is the target.
* `closest <CSS selector>` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next` which resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling)
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous .error` will target the closest previous sibling with `error` class)
Here is an example that targets a div:
\`\`\`html
<div>
<div id="response-div"></div>
<button hx-post="/register" hx-target="#response-div" hx-swap="beforeend">
Register!
</button>
</div>
\`\`\`
The response from the `/register` url will be appended to the `div` with the id `response-div`.
This example uses `hx-target="this"` to make a link that updates itself when clicked:
\`\`\`html
<a hx-post="/new-link" hx-target="this" hx-swap="outerHTML">New link</a>
\`\`\`
## Notes
* `hx-target` is inherited and can be placed on a parent element
```
# attributes/hx-trigger.md
```md
+++
title = "hx-trigger"
description = """\
The hx-trigger attribute in htmx allows you to specify what triggers an AJAX request. Supported triggers include \
standard DOM events, custom events, polling intervals, and event modifiers. The hx-trigger attribute also allows \
specifying event filtering, timing controls, event bubbling, and multiple trigger definitions for fine-grained \
control over when and how requests are initiated."""
+++
The `hx-trigger` attribute allows you to specify what triggers an AJAX request. A trigger
value can be one of the following:
* An event name (e.g. "click" or "my-custom-event") followed by an event filter and a set of event modifiers
* A polling definition of the form `every <timing declaration>`
* A comma-separated list of such events
### Standard Events
Standard events refer to [web API events](https://developer.mozilla.org/en-US/docs/Web/API/Element#events) (e.g. click, keydown, mouseup, load).
A standard event, such as `click` can be specified as the trigger like so:
\`\`\`html
<div hx-get="/clicked" hx-trigger="click">Click Me</div>
\`\`\`
#### Standard Event Filters
Events can be filtered by enclosing a boolean javascript expression in square brackets after the event name. If
this expression evaluates to `true` the event will be triggered, otherwise it will be ignored. Standard event filters [require eval](@/docs.md#configuration-options).
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">Control Click Me</div>
\`\`\`
This event will trigger if a click event is triggered with the `event.ctrlKey` property set to true.
Conditions can also refer to global functions or state
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[checkGlobalState()]">Control Click Me</div>
\`\`\`
And can also be combined using the standard javascript syntax
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[ctrlKey&&shiftKey]">Control-Shift Click Me</div>
\`\`\`
Note that all symbols used in the expression will be resolved first against the triggering event, and then next
against the global namespace, so `myEvent[foo]` will first look for a property named `foo` on the event, then look
for a global symbol with the name `foo`
#### Standard Event Modifiers
Standard events can also have modifiers that change how they behave. The modifiers are:
* `once` - the event will only trigger once (e.g. the first click)
* `changed` - the event will only fire if the value of the element has changed. Please pay attention `change` is the name of the event and `changed` is the name of the modifier.
* `delay:<timing declaration>` - a delay will occur before an event triggers a request. If the event
is seen again it will reset the delay.
* `throttle:<timing declaration>` - a throttle will occur after an event triggers a request. If the event
is seen again before the delay completes, it is ignored, the element will trigger at the end of the delay.
* `from:<Extended CSS selector>` - allows the event that triggers a request to come from another element in the document (e.g. listening to a key event on the body, to support hot keys)
* A standard CSS selector resolves to all elements matching that selector. Thus, `from:input` would listen on every input on the page.
* The CSS selector is only evaluated once and is not re-evaluated when the page changes. If you need to detect dynamically added elements use a [standard event filter](#standard-event-filters), for example `hx-trigger="click[event.target.matches('button')] from:body"` which would [catch](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Event_bubbling) click events from every button on the page.
* The extended CSS selector here allows for the following non-standard CSS values:
* `document` - listen for events on the document
* `window` - listen for events on the window
* `closest <CSS selector>` - finds the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest) ancestor element or itself, matching the given css selector
* `find <CSS selector>` - finds the closest child matching the given css selector
* `next` resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling)
* `next <CSS selector>` scans the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous` resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
* `previous <CSS selector>` scans the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous .error` will target the closest previous sibling with `error` class)
* `target:<CSS selector>` - allows you to filter via a CSS selector on the target of the event. This can be useful when you want to listen for
triggers from elements that might not be in the DOM at the point of initialization, by, for example, listening on the body,
but with a target filter for a child element
* `consume` - if this option is included the event will not trigger any other htmx requests on parents (or on elements
listening on parents)
* `queue:<queue option>` - determines how events are queued if an event occurs while a request for another event is in flight. Options are:
* `first` - queue the first event
* `last` - queue the last event (default)
* `all` - queue all events (issue a request for each event)
* `none` - do not queue new events
Here is an example of a search box that searches on `input`, but only if the search value has changed
and the user hasn't typed anything new for 1 second:
\`\`\`html
<input name="q"
hx-get="/search" hx-trigger="input changed delay:1s"
hx-target="#search-results"/>
\`\`\`
The response from the `/search` url will be appended to the `div` with the id `search-results`.
### Non-standard Events
There are some additional non-standard events that htmx supports:
* `load` - triggered on load (useful for lazy-loading something)
* `revealed` - triggered when an element is scrolled into the viewport (also useful for lazy-loading). If you are using `overflow` in css like `overflow-y: scroll` you should use `intersect once` instead of `revealed`.
* `intersect` - fires once when an element first intersects the viewport. This supports two additional options:
* `root:<selector>` - a CSS selector of the root element for intersection
* `threshold:<float>` - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
### Triggering via the `HX-Trigger` header
If you're trying to fire an event from <code>HX-Trigger</code> response header, you will likely want to
use the `from:body` modifier. E.g. if you send a header like this <code>HX-Trigger: my-custom-event</code>
with a response, an element would likely need to look like this:
\`\`\`html
<div hx-get="/example" hx-trigger="my-custom-event from:body">
Triggered by HX-Trigger header...
</div>
\`\`\`
in order to fire.
This is because the header will likely trigger the event in a different DOM hierarchy than the element that you
wish to be triggered. For a similar reason, you will often listen for hot keys from the body.
### Polling
By using the syntax `every <timing declaration>` you can have an element poll periodically:
\`\`\`html
<div hx-get="/latest_updates" hx-trigger="every 1s">
Nothing Yet!
</div>
\`\`\`
This example will issue a `GET` to the `/latest_updates` URL every second and swap the results into
the innerHTML of this div.
If you want to add a filter to polling, it should be added *after* the poll declaration:
\`\`\`html
<div hx-get="/latest_updates" hx-trigger="every 1s [someConditional]">
Nothing Yet!
</div>
\`\`\`
### Multiple Triggers
Multiple triggers can be provided, separated by commas. Each trigger gets its own options.
\`\`\`html
<div hx-get="/news" hx-trigger="load, click delay:1s"></div>
\`\`\`
This example will load `/news` immediately on page load, and then again with a delay of one second after each click.
### Via JavaScript
The AJAX request can be triggered via JavaScript [`htmx.trigger()`](@/api.md#trigger), too.
## Notes
* `hx-trigger` is not inherited
* `hx-trigger` can be used without an AJAX request, in which case it will only fire the `htmx:trigger` event
* In order to pass a CSS selector that contains whitespace (e.g. `form input`) to the `from`- or `target`-modifier, surround the selector in parentheses or curly brackets (e.g. `from:(form input)` or `from:closest (form input)`)
* A reset event in hx-trigger (e.g. hx-trigger="change, reset") might not work as intended, since HTMX builds its values and sends a request before the browser resets the form values. As a workaround, add a delay to let the browser reset the form before making the request (e.g. hx-trigger="change, reset delay:0.01s").
```
# attributes/hx-validate.md
```md
+++
title = "hx-validate"
description = """\
The hx-validate attribute in htmx will cause an element to validate itself using the HTML5 Validation API before it \
submits a request."""
+++
The `hx-validate` attribute will cause an element to validate itself by way of the [HTML5 Validation API](@/docs.md#validation)
before it submits a request.
Only `<form>` elements validate data by default, but other elements do not. Adding `hx-validate="true"` to `<input>`, `<textarea>` or `<select>` enables validation before sending requests.
## Notes
* `hx-validate` is not inherited
```
# attributes/hx-vals.md
```md
+++
title = "hx-vals"
description = """\
The hx-vals attribute in htmx allows you to add to the parameters that will be submitted with an AJAX request."""
+++
The `hx-vals` attribute allows you to add to the parameters that will be submitted with an AJAX request.
By default, the value of this attribute is a list of name-expression values in [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html)
format.
If you wish for `hx-vals` to *evaluate* the values given, you can prefix the values with `javascript:` or `js:`.
\`\`\`html
<div hx-get="/example" hx-vals='{"myVal": "My Value"}'>Get Some HTML, Including A Value in the Request</div>
<div hx-get="/example" hx-vals='js:{myVal: calculateValue()}'>Get Some HTML, Including a Dynamic Value from Javascript in the Request</div>
\`\`\`
When using evaluated code you can access the `event` object. This example includes the value of the last typed key within the input.
\`\`\`html
<div hx-get="/example" hx-trigger="keyup" hx-vals='js:{lastKey: event.key}'>
<input type="text" />
</div>
\`\`\`
You can also use the spread operator to dynamically specify values. This allows you to include all properties from an object returned by a function:
\`\`\`html
<div hx-get="/example" hx-vals='js:{...foo()}'>Get Some HTML, Including All Values from foo() in the Request</div>
\`\`\`
In this example, if `foo()` returns an object like `{name: "John", age: 30}`, both `name` and `age` will be included as parameters in the request.
## Security Considerations
* By default, the value of `hx-vals` must be valid [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON).
It is **not** dynamically computed. If you use the `javascript:` prefix, be aware that you are introducing
security considerations, especially when dealing with user input such as query strings or user-generated content,
which could introduce a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability.
## Notes
* `hx-vals` is inherited and can be placed on a parent element.
* A child declaration of a variable overrides a parent declaration.
* Input values with the same name will be overridden by variable declarations.
```
# attributes/hx-vars.md
```md
+++
title = "hx-vars"
description = """\
The hx-vars attribute in htmx allows you to dynamically add to the parameters that will be submitted with an AJAX \
request. This attribute has been deprecated. We recommend you use the hx-vals attribute that provides the same \
functionality with safer defaults."""
+++
**NOTE: `hx-vars` has been deprecated in favor of [`hx-vals`](@/attributes/hx-vals.md), which is safer by default.**
The `hx-vars` attribute allows you to dynamically add to the parameters that will be submitted with an AJAX request.
The value of this attribute is a comma separated list of `name`:`<expression>` values, the same as the internal
syntax of javascript [Object Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Object_literals).
\`\`\`html
<div hx-get="/example" hx-vars="myVar:computeMyVar()">Get Some HTML, Including A Dynamic Value in the Request</div>
\`\`\`
## Security Considerations
* The expressions in `hx-vars` are dynamically computed which allows you to add JavaScript code that will be executed. Be careful to **never** trust user input in your expressions as this may lead to a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability. If you are dealing with user input such as query strings or user-generated content, consider using [hx-vals](@/attributes/hx-vals.md) which is a safer alternative.
## Notes
* `hx-vars` is inherited and can be placed on a parent element.
* A child declaration of a variable overrides a parent declaration.
* Input values with the same name will be overridden by variable declarations.
```
# docs.md
```md
+++
title = "Documentation"
[extra]
custom_classes = "wide-content"
+++
<div class="row">
<div class="2 col nav">
**Contents**
<div id="contents">
* [introduction](#introduction)
* [installing](#installing)
* [ajax](#ajax)
* [triggers](#triggers)
* [trigger modifiers](#trigger-modifiers)
* [trigger filters](#trigger-filters)
* [special events](#special-events)
* [polling](#polling)
* [load polling](#load_polling)
* [indicators](#indicators)
* [targets](#targets)
* [swapping](#swapping)
* [synchronization](#synchronization)
* [css transitions](#css_transitions)
* [out of band swaps](#oob_swaps)
* [parameters](#parameters)
* [confirming](#confirming)
* [inheritance](#inheritance)
* [boosting](#boosting)
* [websockets & SSE](#websockets-and-sse)
* [history](#history)
* [requests & responses](#requests)
* [validation](#validation)
* [animations](#animations)
* [extensions](#extensions)
* [events & logging](#events)
* [debugging](#debugging)
* [scripting](#scripting)
* [hx-on attribute](#hx-on)
* [3rd party integration](#3rd-party)
* [Web Components](#web-components)
* [caching](#caching)
* [security](#security)
* [configuring](#config)
</div>
</div>
<div class="10 col">
## htmx in a Nutshell {#introduction}
htmx is a library that allows you to access modern browser features directly from HTML, rather than using
javascript.
To understand htmx, first let's take a look at an anchor tag:
\`\`\`html
<a href="/blog">Blog</a>
\`\`\`
This anchor tag tells a browser:
> "When a user clicks on this link, issue an HTTP GET request to '/blog' and load the response content
> into the browser window".
With that in mind, consider the following bit of HTML:
\`\`\`html
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML">
Click Me!
</button>
\`\`\`
This tells htmx:
> "When a user clicks on this button, issue an HTTP POST request to '/clicked' and use the content from the response
> to replace the element with the id `parent-div` in the DOM"
htmx extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly
within the language:
* Now any element, not just anchors and forms, can issue an HTTP request
* Now any event, not just clicks or form submissions, can trigger requests
* Now any [HTTP verb](https://en.wikipedia.org/wiki/HTTP_Verbs), not just `GET` and `POST`, can be used
* Now any element, not just the entire window, can be the target for update by the request
Note that when you are using htmx, on the server side you typically respond with *HTML*, not *JSON*. This keeps you firmly
within the [original web programming model](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm),
using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)
without even needing to really understand that concept.
It's worth mentioning that, if you prefer, you can use the [`data-`](https://html.spec.whatwg.org/multipage/dom.html#attr-data-*) prefix when using htmx:
\`\`\`html
<a data-hx-post="/click">Click Me!</a>
\`\`\`
If you understand the concepts around htmx and want to see the quirks of the library, please see our
[QUIRKS](@/QUIRKS.md) page.
## 1.x to 2.x Migration Guide
[Version 1](https://v1.htmx.org) of htmx is still supported and supports IE11, but the latest version of htmx is 2.x.
If you are migrating to htmx 2.x from [htmx 1.x](https://v1.htmx.org), please see the [htmx 1.x migration guide](@/migration-guide-htmx-1.md).
If you are migrating to htmx from intercooler.js, please see the [intercooler migration guide](@/migration-guide-intercooler.md).
## Installing
Htmx is a dependency-free, browser-oriented javascript library. This means that using it is as simple as adding a `<script>`
tag to your document head. There is no need for a build system to use it.
### Via A CDN (e.g. unpkg.com)
The fastest way to get going with htmx is to load it via a CDN. You can simply add this to
your head tag and get going:
\`\`\`html
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
\`\`\`
An unminified version is also available as well:
\`\`\`html
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.js" integrity="sha384-oeUn82QNXPuVkGCkcrInrS1twIxKhkZiFfr2TdiuObZ3n3yIeMiqcRzkIcguaof1" crossorigin="anonymous"></script>
\`\`\`
While the CDN approach is extremely simple, you may want to consider
[not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn).
### Download a copy
The next easiest way to install htmx is to simply copy it into your project.
Download `htmx.min.js` [from unpkg.com](https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js) and add it to the appropriate directory in your project
and include it where necessary with a `<script>` tag:
\`\`\`html
<script src="/path/to/htmx.min.js"></script>
\`\`\`
### npm
For npm-style build systems, you can install htmx via [npm](https://www.npmjs.com/):
\`\`\`sh
npm install htmx.org@2.0.4
\`\`\`
After installing, you’ll need to use appropriate tooling to use `node_modules/htmx.org/dist/htmx.js` (or `.min.js`).
For example, you might bundle htmx with some extensions and project-specific code.
### Webpack
If you are using webpack to manage your javascript:
* Install `htmx` via your favourite package manager (like npm or yarn)
* Add the import to your `index.js`
\`\`\`js
import 'htmx.org';
\`\`\`
If you want to use the global `htmx` variable (recommended), you need to inject it to the window scope:
* Create a custom JS file
* Import this file to your `index.js` (below the import from step 2)
\`\`\`js
import 'path/to/my_custom.js';
\`\`\`
* Then add this code to the file:
\`\`\`js
window.htmx = require('htmx.org');
\`\`\`
* Finally, rebuild your bundle
## AJAX
The core of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML:
| Attribute | Description |
|----------------------------------------|--------------------------------------------|
| [hx-get](@/attributes/hx-get.md) | Issues a `GET` request to the given URL |
| [hx-post](@/attributes/hx-post.md) | Issues a `POST` request to the given URL |
| [hx-put](@/attributes/hx-put.md) | Issues a `PUT` request to the given URL |
| [hx-patch](@/attributes/hx-patch.md) | Issues a `PATCH` request to the given URL |
| [hx-delete](@/attributes/hx-delete.md) | Issues a `DELETE` request to the given URL |
Each of these attributes takes a URL to issue an AJAX request to. The element will issue a request of the specified
type to the given URL when the element is [triggered](#triggers):
\`\`\`html
<button hx-put="/messages">
Put To Messages
</button>
\`\`\`
This tells the browser:
> When a user clicks on this button, issue a PUT request to the URL /messages and load the response into the button
### Triggering Requests {#triggers}
By default, AJAX requests are triggered by the "natural" event of an element:
* `input`, `textarea` & `select` are triggered on the `change` event
* `form` is triggered on the `submit` event
* everything else is triggered by the `click` event
If you want different behavior you can use the [hx-trigger](@/attributes/hx-trigger.md)
attribute to specify which event will cause the request.
Here is a `div` that posts to `/mouse_entered` when a mouse enters it:
\`\`\`html
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
\`\`\`
#### Trigger Modifiers
A trigger can also have a few additional modifiers that change its behavior. For example, if you want a request to only
happen once, you can use the `once` modifier for the trigger:
\`\`\`html
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[Here Mouse, Mouse!]
</div>
\`\`\`
Other modifiers you can use for triggers are:
* `changed` - only issue a request if the value of the element has changed
* `delay:<time interval>` - wait the given amount of time (e.g. `1s`) before
issuing the request. If the event triggers again, the countdown is reset.
* `throttle:<time interval>` - wait the given amount of time (e.g. `1s`) before
issuing the request. Unlike `delay` if a new event occurs before the time limit is hit the event will be discarded,
so the request will trigger at the end of the time period.
* `from:<CSS Selector>` - listen for the event on a different element. This can be used for things like keyboard shortcuts. Note that this CSS selector is not re-evaluated if the page changes.
You can use these attributes to implement many common UX patterns, such as [Active Search](@/examples/active-search.md):
\`\`\`html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search...">
<div id="search-results"></div>
\`\`\`
This input will issue a request 500 milliseconds after a key up event if the input has been changed and inserts the results
into the `div` with the id `search-results`.
Multiple triggers can be specified in the [hx-trigger](@/attributes/hx-trigger.md) attribute, separated by commas.
#### Trigger Filters
You may also apply trigger filters by using square brackets after the event name, enclosing a javascript expression that
will be evaluated. If the expression evaluates to `true` the event will trigger, otherwise it will not.
Here is an example that triggers only on a Control-Click of the element
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Control Click Me
</div>
\`\`\`
Properties like `ctrlKey` will be resolved against the triggering event first, then against the global scope. The
`this` symbol will be set to the current element.
#### Special Events
htmx provides a few special events for use in [hx-trigger](@/attributes/hx-trigger.md):
* `load` - fires once when the element is first loaded
* `revealed` - fires once when an element first scrolls into the viewport
* `intersect` - fires once when an element first intersects the viewport. This supports two additional options:
* `root:<selector>` - a CSS selector of the root element for intersection
* `threshold:<float>` - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
You can also use custom events to trigger requests if you have an advanced use case.
#### Polling
If you want an element to poll the given URL rather than wait for an event, you can use the `every` syntax
with the [`hx-trigger`](@/attributes/hx-trigger.md) attribute:
\`\`\`html
<div hx-get="/news" hx-trigger="every 2s"></div>
\`\`\`
This tells htmx
> Every 2 seconds, issue a GET to /news and load the response into the div
If you want to stop polling from a server response you can respond with the HTTP response code [`286`](https://en.wikipedia.org/wiki/86_(term))
and the element will cancel the polling.
#### Load Polling {#load_polling}
Another technique that can be used to achieve polling in htmx is "load polling", where an element specifies
a `load` trigger along with a delay, and replaces itself with the response:
\`\`\`html
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML">
</div>
\`\`\`
If the `/messages` end point keeps returning a div set up this way, it will keep "polling" back to the URL every
second.
Load polling can be useful in situations where a poll has an end point at which point the polling terminates, such as
when you are showing the user a [progress bar](@/examples/progress-bar.md).
### Request Indicators {#indicators}
When an AJAX request is issued it is often good to let the user know that something is happening since the browser
will not give them any feedback. You can accomplish this in htmx by using `htmx-indicator` class.
The `htmx-indicator` class is defined so that the opacity of any element with this class is 0 by default, making it invisible
but present in the DOM.
When htmx issues a request, it will put a `htmx-request` class onto an element (either the requesting element or
another element, if specified). The `htmx-request` class will cause a child element with the `htmx-indicator` class
on it to transition to an opacity of 1, showing the indicator.
\`\`\`html
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
\`\`\`
Here we have a button. When it is clicked the `htmx-request` class will be added to it, which will reveal the spinner
gif element. (I like [SVG spinners](http://samherbert.net/svg-loaders/) these days.)
While the `htmx-indicator` class uses opacity to hide and show the progress indicator, if you would prefer another mechanism
you can create your own CSS transition like so:
\`\`\`css
.htmx-indicator{
display:none;
}
.htmx-request .htmx-indicator{
display:inline;
}
.htmx-request.htmx-indicator{
display:inline;
}
\`\`\`
If you want the `htmx-request` class added to a different element, you can use the [hx-indicator](@/attributes/hx-indicator.md)
attribute with a CSS selector to do so:
\`\`\`html
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
\`\`\`
Here we call out the indicator explicitly by id. Note that we could have placed the class on the parent `div` as well
and had the same effect.
You can also add the [`disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled) to
elements for the duration of a request by using the [hx-disabled-elt](@/attributes/hx-disabled-elt.md) attribute.
### Targets
If you want the response to be loaded into a different element other than the one that made the request, you can
use the [hx-target](@/attributes/hx-target.md) attribute, which takes a CSS selector. Looking back at our Live Search example:
\`\`\`html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search...">
<div id="search-results"></div>
\`\`\`
You can see that the results from the search are going to be loaded into `div#search-results`, rather than into the
input tag.
#### Extended CSS Selectors {#extended-css-selectors}
`hx-target`, and most attributes that take a CSS selector, support an "extended" CSS syntax:
* You can use the `this` keyword, which indicates that the element that the `hx-target` attribute is on is the target
* The `closest <CSS selector>` syntax will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector.
(e.g. `closest tr` will target the closest table row to the element)
* The `next <CSS selector>` syntax will find the next element in the DOM matching the given CSS selector.
* The `previous <CSS selector>` syntax will find the previous element in the DOM matching the given CSS selector.
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
(e.g `find tr` would target the first child descendant row to the element)
In addition, a CSS selector may be wrapped in `<` and `/>` characters, mimicking the
[query literal](https://hyperscript.org/expressions/query-reference/) syntax of hyperscript.
Relative targets like this can be useful for creating flexible user interfaces without peppering your DOM with lots
of `id` attributes.
### Swapping {#swapping}
htmx offers a few different ways to swap the HTML returned into the DOM. By default, the content replaces the
`innerHTML` of the target element. You can modify this by using the [hx-swap](@/attributes/hx-swap.md) attribute
with any of the following values:
| Name | Description
|------|-------------
| `innerHTML` | the default, puts the content inside the target element
| `outerHTML` | replaces the entire target element with the returned content
| `afterbegin` | prepends the content before the first child inside the target
| `beforebegin` | prepends the content before the target in the target's parent element
| `beforeend` | appends the content after the last child inside the target
| `afterend` | appends the content after the target in the target's parent element
| `delete` | deletes the target element regardless of the response
| `none` | does not append content from response ([Out of Band Swaps](#oob_swaps) and [Response Headers](#response-headers) will still be processed)
#### Morph Swaps {#morphing}
In addition to the standard swap mechanisms above, htmx also supports _morphing_ swaps, via extensions. Morphing swaps
attempt to _merge_ new content into the existing DOM, rather than simply replacing it. They often do a better job
preserving things like focus, video state, etc. by mutating existing nodes in-place during the swap operation, at the
cost of more CPU.
The following extensions are available for morph-style swaps:
* [Idiomorph](/extensions/idiomorph) - A morphing algorithm created by the htmx developers.
* [Morphdom Swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) - Based on the [morphdom](https://github.com/patrick-steele-idem/morphdom),
the original DOM morphing library.
* [Alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) - Based on the [alpine morph](https://alpinejs.dev/plugins/morph) plugin, plays
well with alpine.js
#### View Transitions {#view-transitions}
The new, experimental [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
gives developers a way to create an animated transition between different DOM states. It is still in active development
and is not available in all browsers, but htmx provides a way to work with this new API that falls back to the non-transition
mechanism if the API is not available in a given browser.
You can experiment with this new API using the following approaches:
* Set the `htmx.config.globalViewTransitions` config variable to `true` to use transitions for all swaps
* Use the `transition:true` option in the `hx-swap` attribute
* If an element swap is going to be transitioned due to either of the above configurations, you may catch the
`htmx:beforeTransition` event and call `preventDefault()` on it to cancel the transition.
View Transitions can be configured using CSS, as outlined in [the Chrome documentation for the feature](https://developer.chrome.com/docs/web-platform/view-transitions/#simple-customization).
You can see a view transition example on the [Animation Examples](/examples/animations#view-transitions) page.
#### Swap Options
The [hx-swap](@/attributes/hx-swap.md) attribute supports many options for tuning the swapping behavior of htmx. For
example, by default htmx will swap in the title of a title tag found anywhere in the new content. You can turn this
behavior off by setting the `ignoreTitle` modifier to true:
\`\`\`html
<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">Like</button>
\`\`\`
The modifiers available on `hx-swap` are:
| Option | Description |
|---------------|----------------------------------------------------------------------------------------------------------|
| `transition` | `true` or `false`, whether to use the view transition API for this swap |
| `swap` | The swap delay to use (e.g. `100ms`) between when old content is cleared and the new content is inserted |
| `settle` | The settle delay to use (e.g. `100ms`) between when new content is inserted and when it is settled |
| `ignoreTitle` | If set to `true`, any title found in the new content will be ignored and not update the document title |
| `scroll` | `top` or `bottom`, will scroll the target element to its top or bottom |
| `show` | `top` or `bottom`, will scroll the target element's top or bottom into view |
All swap modifiers appear after the swap style is specified, and are colon-separated.
See the [hx-swap](@/attributes/hx-swap.md) documentation for more details on these options.
### Synchronization {#synchronization}
Often you want to coordinate the requests between two elements. For example, you may want a request from one element
to supersede the request of another element, or to wait until the other element's request has finished.
htmx offers a [`hx-sync`](@/attributes/hx-sync.md) attribute to help you accomplish this.
Consider a race condition between a form submission and an individual input's validation request in this HTML:
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change">
<button type="submit">Submit</button>
</form>
\`\`\`
Without using `hx-sync`, filling out the input and immediately submitting the form triggers two parallel requests to
`/validate` and `/store`.
Using `hx-sync="closest form:abort"` on the input will watch for requests on the form and abort the input's request if
a form request is present or starts while the input request is in flight:
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
\`\`\`
This resolves the synchronization between the two elements in a declarative way.
htmx also supports a programmatic way to cancel requests: you can send the `htmx:abort` event to an element to
cancel any in-flight requests:
\`\`\`html
<button id="request-button" hx-post="/example">
Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Request
</button>
\`\`\`
More examples and details can be found on the [`hx-sync` attribute page.](@/attributes/hx-sync.md)
### CSS Transitions {#css_transitions}
htmx makes it easy to use [CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) without
javascript. Consider this HTML content:
\`\`\`html
<div id="div1">Original Content</div>
\`\`\`
Imagine this content is replaced by htmx via an ajax request with this new content:
\`\`\`html
<div id="div1" class="red">New Content</div>
\`\`\`
Note two things:
* The div has the *same* id in the original and in the new content
* The `red` class has been added to the new content
Given this situation, we can write a CSS transition from the old state to the new state:
\`\`\`css
.red {
color: red;
transition: all ease-in 1s ;
}
\`\`\`
When htmx swaps in this new content, it will do so in such a way that the CSS transition will apply to the new content,
giving you a nice, smooth transition to the new state.
So, in summary, all you need to do to use CSS transitions for an element is keep its `id` stable across requests!
You can see the [Animation Examples](@/examples/animations.md) for more details and live demonstrations.
#### Details
To understand how CSS transitions actually work in htmx, you must understand the underlying swap & settle model that htmx uses.
When new content is received from a server, before the content is swapped in, the existing
content of the page is examined for elements that match by the `id` attribute. If a match
is found for an element in the new content, the attributes of the old content are copied
onto the new element before the swap occurs. The new content is then swapped in, but with the
*old* attribute values. Finally, the new attribute values are swapped in, after a "settle" delay
(20ms by default). A little crazy, but this is what allows CSS transitions to work without any javascript by
the developer.
### Out of Band Swaps {#oob_swaps}
If you want to swap content from a response directly into the DOM by using the `id` attribute you can use the
[hx-swap-oob](@/attributes/hx-swap-oob.md) attribute in the *response* html:
\`\`\`html
<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content
\`\`\`
In this response, `div#message` would be swapped directly into the matching DOM element, while the additional content
would be swapped into the target in the normal manner.
You can use this technique to "piggy-back" updates on other requests.
#### Troublesome Tables
Table elements can be problematic when combined with out of band swaps, because, by the HTML spec, many can't stand on
their own in the DOM (e.g. `<tr>` or `<td>`).
To avoid this issue you can use a `template` tag to encapsulate these elements:
\`\`\`html
<template>
<tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>
\`\`\`
#### Selecting Content To Swap
If you want to select a subset of the response HTML to swap into the target, you can use the [hx-select](@/attributes/hx-select.md)
attribute, which takes a CSS selector and selects the matching elements from the response.
You can also pick out pieces of content for an out-of-band swap by using the [hx-select-oob](@/attributes/hx-select-oob.md)
attribute, which takes a list of element IDs to pick out and swap.
#### Preserving Content During A Swap
If there is content that you wish to be preserved across swaps (e.g. a video player that you wish to remain playing
even if a swap occurs) you can use the [hx-preserve](@/attributes/hx-preserve.md)
attribute on the elements you wish to be preserved.
### Parameters
By default, an element that causes a request will include its value if it has one. If the element is a form it
will include the values of all inputs within it.
As with HTML forms, the `name` attribute of the input is used as the parameter name in the request that htmx sends.
Additionally, if the element causes a non-`GET` request, the values of all the inputs of the nearest enclosing form
will be included.
If you wish to include the values of other elements, you can use the [hx-include](@/attributes/hx-include.md) attribute
with a CSS selector of all the elements whose values you want to include in the request.
If you wish to filter out some parameters you can use the [hx-params](@/attributes/hx-params.md) attribute.
Finally, if you want to programmatically modify the parameters, you can use the [htmx:configRequest](@/events.md#htmx:configRequest)
event.
#### File Upload {#files}
If you wish to upload files via an htmx request, you can set the [hx-encoding](@/attributes/hx-encoding.md) attribute to
`multipart/form-data`. This will use a `FormData` object to submit the request, which will properly include the file
in the request.
Note that depending on your server-side technology, you may have to handle requests with this type of body content very
differently.
Note that htmx fires a `htmx:xhr:progress` event periodically based on the standard `progress` event during upload,
which you can hook into to show the progress of the upload.
See the [examples section](@/examples/_index.md) for more advanced form patterns, including [progress bars](@/examples/file-upload.md) and [error handling](@/examples/file-upload-input.md).
#### Extra Values
You can include extra values in a request using the [hx-vals](@/attributes/hx-vals.md) (name-expression pairs in JSON format) and
[hx-vars](@/attributes/hx-vars.md) attributes (comma-separated name-expression pairs that are dynamically computed).
### Confirming Requests {#confirming}
Often you will want to confirm an action before issuing a request. htmx supports the [`hx-confirm`](@/attributes/hx-confirm.md)
attribute, which allows you to confirm an action using a simple javascript dialog:
\`\`\`html
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
\`\`\`
Using events you can implement more sophisticated confirmation dialogs. The [confirm example](@/examples/confirm.md)
shows how to use [sweetalert2](https://sweetalert2.github.io/) library for confirmation of htmx actions.
#### Confirming Requests Using Events
Another option to do confirmation with is via the [`htmx:confirm` event](@/events.md#htmx:confirm). This event
is fired on *every* trigger for a request (not just on elements that have a `hx-confirm` attribute) and can be used
to implement asynchronous confirmation of the request.
Here is an example using [sweet alert](https://sweetalert.js.org/guides/) on any element with a `confirm-with-sweet-alert='true'` attribute on it:
\`\`\`javascript
document.body.addEventListener('htmx:confirm', function(evt) {
if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
evt.preventDefault();
swal({
title: "Are you sure?",
text: "Are you sure you are sure?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
evt.detail.issueRequest();
}
});
}
});
\`\`\`
## Attribute Inheritance {#inheritance}
Most attributes in htmx are inherited: they apply to the element they are on as well as any children elements. This
allows you to "hoist" attributes up the DOM to avoid code duplication. Consider the following htmx:
\`\`\`html
<button hx-delete="/account" hx-confirm="Are you sure?">
Delete My Account
</button>
<button hx-put="/account" hx-confirm="Are you sure?">
Update My Account
</button>
\`\`\`
Here we have a duplicate `hx-confirm` attribute. We can hoist this attribute to a parent element:
\`\`\`html
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
\`\`\`
This `hx-confirm` attribute will now apply to all htmx-powered elements within it.
Sometimes you wish to undo this inheritance. Consider if we had a cancel button to this group, but didn't want it to
be confirmed. We could add an `unset` directive on it like so:
\`\`\`html
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
<button hx-confirm="unset" hx-get="/">
Cancel
</button>
</div>
\`\`\`
The top two buttons would then show a confirm dialog, but the bottom cancel button would not.
Inheritance can be disabled on a per-element and per-attribute basis using the
[`hx-disinherit`](@/attributes/hx-disinherit.md) attribute.
If you wish to disable attribute inheritance entirely, you can set the `htmx.config.disableInheritance` configuration
variable to `true`. This will disable inheritance as a default, and allow you to specify inheritance explicitly
with the [`hx-inherit`](@/attributes/hx-inherit.md) attribute.
## Boosting
Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](@/attributes/hx-boost.md) attribute. This
attribute will convert all anchor tags and forms into AJAX requests that, by default, target the body of the page.
Here is an example:
\`\`\`html
<div hx-boost="true">
<a href="/blog">Blog</a>
</div>
\`\`\`
The anchor tag in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag.
### Progressive Enhancement {#progressive_enhancement}
A feature of `hx-boost` is that it degrades gracefully if javascript is not enabled: the links and forms continue
to work, they simply don't use ajax requests. This is known as
[Progressive Enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), and it allows
a wider audience to use your site's functionality.
Other htmx patterns can be adapted to achieve progressive enhancement as well, but they will require more thought.
Consider the [active search](@/examples/active-search.md) example. As it is written, it will not degrade gracefully:
someone who does not have javascript enabled will not be able to use this feature. This is done for simplicity’s sake,
to keep the example as brief as possible.
However, you could wrap the htmx-enhanced input in a form element:
\`\`\`html
<form action="/search" method="POST">
<input class="form-control" type="search"
name="search" placeholder="Begin typing to search users..."
hx-post="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
</form>
\`\`\`
With this in place, javascript-enabled clients would still get the nice active-search UX, but non-javascript enabled
clients would be able to hit the enter key and still search. Even better, you could add a "Search" button as well.
You would then need to update the form with an `hx-post` that mirrored the `action` attribute, or perhaps use `hx-boost`
on it.
You would need to check on the server side for the `HX-Request` header to differentiate between an htmx-driven and a
regular request, to determine exactly what to render to the client.
Other patterns can be adapted similarly to achieve the progressive enhancement needs of your application.
As you can see, this requires more thought and more work. It also rules some functionality entirely out of bounds.
These tradeoffs must be made by you, the developer, with respect to your projects goals and audience.
[Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) is a concept
closely related to progressive enhancement. Using progressive enhancement techniques such as `hx-boost` will make your
htmx application more accessible to a wide array of users.
htmx-based applications are very similar to normal, non-AJAX driven web applications because htmx is HTML-oriented.
As such, the normal HTML accessibility recommendations apply. For example:
* Use semantic HTML as much as possible (i.e. the right tags for the right things)
* Ensure focus state is clearly visible
* Associate text labels with all form fields
* Maximize the readability of your application with appropriate fonts, contrast, etc.
## Web Sockets & SSE {#websockets-and-sse}
Web Sockets and Server Sent Events (SSE) are supported via extensions. Please see
the [SSE extension](/extensions/sse) and [WebSocket extension](/extensions/ws)
pages to learn more.
## History Support {#history}
Htmx provides a simple mechanism for interacting with the [browser history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API):
If you want a given element to push its request URL into the browser navigation bar and add the current state of the page
to the browser's history, include the [hx-push-url](@/attributes/hx-push-url.md) attribute:
\`\`\`html
<a hx-get="/blog" hx-push-url="true">Blog</a>
\`\`\`
When a user clicks on this link, htmx will snapshot the current DOM and store it before it makes a request to /blog.
It then does the swap and pushes a new location onto the history stack.
When a user hits the back button, htmx will retrieve the old content from storage and swap it back into the target,
simulating "going back" to the previous state. If the location is not found in the cache, htmx will make an ajax
request to the given URL, with the header `HX-History-Restore-Request` set to true, and expects back the HTML needed
for the entire page. Alternatively, if the `htmx.config.refreshOnHistoryMiss` config variable is set to true, it will
issue a hard browser refresh.
**NOTE:** If you push a URL into the history, you **must** be able to navigate to that URL and get a full page back!
A user could copy and paste the URL into an email, or new tab. Additionally, htmx will need the entire page when restoring
history if the page is not in the history cache.
### Specifying History Snapshot Element
By default, htmx will use the `body` to take and restore the history snapshot from. This is usually the right thing, but
if you want to use a narrower element for snapshotting you can use the [hx-history-elt](@/attributes/hx-history-elt.md)
attribute to specify a different one.
Careful: this element will need to be on all pages or restoring from history won't work reliably.
### Undoing DOM Mutations By 3rd Party Libraries
If you are using a 3rd party library and want to use the htmx history feature, you will need to clean up the DOM before
a snapshot is taken. Let's consider the [Tom Select](https://tom-select.js.org/) library, which makes select elements
a much richer user experience. Let's set up TomSelect to turn any input element with the `.tomselect` class into a rich
select element.
First we need to initialize elements that have the class in new content:
\`\`\`javascript
htmx.onLoad(function (target) {
// find all elements in the new content that should be
// an editor and init w/ TomSelect
var editors = target.querySelectorAll(".tomselect")
.forEach(elt => new TomSelect(elt))
});
\`\`\`
This will create a rich selector for all input elements that have the `.tomselect` class on it. However, it mutates
the DOM and we don't want that mutation saved to the history cache, since TomSelect will be reinitialized when the
history content is loaded back into the screen.
To deal with this, we need to catch the `htmx:beforeHistorySave` event and clean out the TomSelect mutations by calling
`destroy()` on them:
\`\`\`javascript
htmx.on('htmx:beforeHistorySave', function() {
// find all TomSelect elements
document.querySelectorAll('.tomSelect')
.forEach(elt => elt.tomselect.destroy()) // and call destroy() on them
})
\`\`\`
This will revert the DOM to the original HTML, thus allowing for a clean snapshot.
### Disabling History Snapshots
History snapshotting can be disabled for a URL by setting the [hx-history](@/attributes/hx-history.md) attribute to `false`
on any element in the current document, or any html fragment loaded into the current document by htmx. This can be used
to prevent sensitive data entering the localStorage cache, which can be important for shared-use / public computers.
History navigation will work as expected, but on restoration the URL will be requested from the server instead of the
local history cache.
## Requests &amp; Responses {#requests}
Htmx expects responses to the AJAX requests it makes to be HTML, typically HTML fragments (although a full HTML
document, matched with a [hx-select](@/attributes/hx-select.md) tag can be useful too). Htmx will then swap the returned
HTML into the document at the target specified and with the swap strategy specified.
Sometimes you might want to do nothing in the swap, but still perhaps trigger a client side event ([see below](#response-headers)).
For this situation, by default, you can return a `204 - No Content` response code, and htmx will ignore the content of
the response.
In the event of an error response from the server (e.g. a 404 or a 501), htmx will trigger the [`htmx:responseError`](@/events.md#htmx:responseError)
event, which you can handle.
In the event of a connection error, the [`htmx:sendError`](@/events.md#htmx:sendError) event will be triggered.
### Configuring Response Handling {#response-handling}
You can configure the above behavior of htmx by mutating or replacing the `htmx.config.responseHandling` array. This
object is a collection of JavaScript objects defined like so:
\`\`\`js
responseHandling: [
{code:"204", swap: false}, // 204 - No Content by default does nothing, but is not an error
{code:"[23]..", swap: true}, // 200 & 300 responses are non-errors and are swapped
{code:"[45]..", swap: false, error:true}, // 400 & 500 responses are not swapped and are errors
{code:"...", swap: false} // catch all for any other response code
]
\`\`\`
When htmx receives a response it will iterate in order over the `htmx.config.responseHandling` array and test if the
`code` property of a given object, when treated as a Regular Expression, matches the current response. If an entry
does match the current response code, it will be used to determine if and how the response will be processed.
The fields available for response handling configuration on entries in this array are:
* `code` - a String representing a regular expression that will be tested against response codes.
* `swap` - `true` if the response should be swapped into the DOM, `false` otherwise
* `error` - `true` if htmx should treat this response as an error
* `ignoreTitle` - `true` if htmx should ignore title tags in the response
* `select` - A CSS selector to use to select content from the response
* `target` - A CSS selector specifying an alternative target for the response
* `swapOverride` - An alternative swap mechanism for the response
#### Configuring Response Handling Examples {#response-handling-examples}
As an example of how to use this configuration, consider a situation when a server-side framework responds with a
[`422 - Unprocessable Entity`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) response when validation errors occur. By default, htmx will ignore the response,
since it matches the Regular Expression `[45]..`.
Using the [meta config](#configuration-options) mechanism for configuring responseHandling, we could add the following
config:
\`\`\`html
<!--
* 204 No Content by default does nothing, but is not an error
* 2xx, 3xx and 422 responses are non-errors and are swapped
* 4xx & 5xx responses are not swapped and are errors
* all other responses are swapped using "..." as a catch-all
-->
<meta
name="htmx-config"
content='{
"responseHandling":[
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": true}
]
}'
/>
\`\`\`
If you wanted to swap everything, regardless of HTTP response code, you could use this configuration:
\`\`\`html
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!--all responses are swapped-->
\`\`\`
Finally, it is worth considering using the [Response Targets](/extensions/response-targets)
extension, which allows you to configure the behavior of response codes declaratively via attributes.
### CORS
When using htmx in a cross origin context, remember to configure your web
server to set Access-Control headers in order for htmx headers to be visible
on the client side.
- [Access-Control-Allow-Headers (for request headers)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers)
- [Access-Control-Expose-Headers (for response headers)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)
[See all the request and response headers that htmx implements.](@/reference.md#request_headers)
### Request Headers
htmx includes a number of useful headers in requests:
| Header | Description |
|--------|-------------|
| `HX-Boosted` | indicates that the request is via an element using [hx-boost](@/attributes/hx-boost.md)
| `HX-Current-URL` | the current URL of the browser
| `HX-History-Restore-Request` | "true" if the request is for history restoration after a miss in the local history cache
| `HX-Prompt` | the user response to an [hx-prompt](@/attributes/hx-prompt.md)
| `HX-Request` | always "true"
| `HX-Target` | the `id` of the target element if it exists
| `HX-Trigger-Name` | the `name` of the triggered element if it exists
| `HX-Trigger` | the `id` of the triggered element if it exists
### Response Headers
htmx supports some htmx-specific response headers:
* [`HX-Location`](@/headers/hx-location.md) - allows you to do a client-side redirect that does not do a full page reload
* [`HX-Push-Url`](@/headers/hx-push-url.md) - pushes a new url into the history stack
* [`HX-Redirect`](@/headers/hx-redirect.md) - can be used to do a client-side redirect to a new location
* `HX-Refresh` - if set to "true" the client-side will do a full refresh of the page
* [`HX-Replace-Url`](@/headers/hx-replace-url.md) - replaces the current URL in the location bar
* `HX-Reswap` - allows you to specify how the response will be swapped. See [hx-swap](@/attributes/hx-swap.md) for possible values
* `HX-Retarget` - a CSS selector that updates the target of the content update to a different element on the page
* `HX-Reselect` - a CSS selector that allows you to choose which part of the response is used to be swapped in. Overrides an existing [`hx-select`](@/attributes/hx-select.md) on the triggering element
* [`HX-Trigger`](@/headers/hx-trigger.md) - allows you to trigger client-side events
* [`HX-Trigger-After-Settle`](@/headers/hx-trigger.md) - allows you to trigger client-side events after the settle step
* [`HX-Trigger-After-Swap`](@/headers/hx-trigger.md) - allows you to trigger client-side events after the swap step
For more on the `HX-Trigger` headers, see [`HX-Trigger` Response Headers](@/headers/hx-trigger.md).
Submitting a form via htmx has the benefit of no longer needing the [Post/Redirect/Get Pattern](https://en.wikipedia.org/wiki/Post/Redirect/Get).
After successfully processing a POST request on the server, you don't need to return a [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). You can directly return the new HTML fragment.
Also the response headers above are not provided to htmx for processing with 3xx Redirect response codes like [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). Instead, the browser will intercept the redirection internally and return the headers and response from the redirected URL. Where possible use alternative response codes like 200 to allow returning of these response headers.
### Request Order of Operations {#request-operations}
The order of operations in a htmx request are:
* The element is triggered and begins a request
* Values are gathered for the request
* The `htmx-request` class is applied to the appropriate elements
* The request is then issued asynchronously via AJAX
* Upon getting a response the target element is marked with the `htmx-swapping` class
* An optional swap delay is applied (see the [hx-swap](@/attributes/hx-swap.md) attribute)
* The actual content swap is done
* the `htmx-swapping` class is removed from the target
* the `htmx-added` class is added to each new piece of content
* the `htmx-settling` class is applied to the target
* A settle delay is done (default: 20ms)
* The DOM is settled
* the `htmx-settling` class is removed from the target
* the `htmx-added` class is removed from each new piece of content
You can use the `htmx-swapping` and `htmx-settling` classes to create
[CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) between pages.
## Validation
Htmx integrates with the [HTML5 Validation API](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation)
and will not issue a request for a form if a validatable input is invalid. This is true for both AJAX requests as well as
WebSocket sends.
Htmx fires events around validation that can be used to hook in custom validation and error handling:
* `htmx:validation:validate` - called before an element's `checkValidity()` method is called. May be used to add in
custom validation logic
* `htmx:validation:failed` - called when `checkValidity()` returns false, indicating an invalid input
* `htmx:validation:halted` - called when a request is not issued due to validation errors. Specific errors may be found
in the `event.detail.errors` object
Non-form elements do not validate before they make requests by default, but you can enable validation by setting
the [`hx-validate`](@/attributes/hx-validate.md) attribute to "true".
### Validation Example
Here is an example of an input that uses the [`hx-on`](/attributes/hx-on) attribute to catch the
`htmx:validation:validate` event and require that the input have the value `foo`:
\`\`\`html
<form id="example-form" hx-post="/test">
<input name="example"
onkeyup="this.setCustomValidity('') // reset the validation on keyup"
hx-on:htmx:validation:validate="if(this.value != 'foo') {
this.setCustomValidity('Please enter the value foo') // set the validation error
htmx.find('#example-form').reportValidity() // report the issue
}">
</form>
\`\`\`
Note that all client side validations must be re-done on the server side, as they can always be bypassed.
## Animations
Htmx allows you to use [CSS transitions](#css_transitions)
in many situations using only HTML and CSS.
Please see the [Animation Guide](@/examples/animations.md) for more details on the options available.
## Extensions
htmx provides an [extensions](/extensions) mechanism that allows you to customize the libraries' behavior.
Extensions [are defined in javascript](/extensions/building) and then enabled via
the [`hx-ext`](@/attributes/hx-ext.md) attribute.
### Core Extensions
htmx supports a few "core" extensions, which are supported by the htmx development team:
* [head-support](/extensions/head-support) - support for merging head tag information (styles, etc.) in htmx requests
* [htmx-1-compat](/extensions/htmx-1-compat) - restores htmx 1 defaults & functionality
* [idiomorph](/extensions/idiomorph) - supports the `morph` swap strategy using idiomorph
* [preload](/extensions/preload) - allows you to preload content for better performance
* [response-targets](/extensions/response-targets) - allows you to target elements based on HTTP response codes (e.g. `404`)
* [sse](/extensions/sse) - support for [Server Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
* [ws](/extensions/ws) - support for [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
You can see all available extensions on the [Extensions](/extensions) page.
### Installing Extensions
The fastest way to install htmx extensions created by others is to load them via a CDN. Remember to always include the core htmx library before the extensions and [enable the extension](#enabling-extensions). For example, if you would like to use the [response-targets](/extensions/response-targets) extension, you can add this to your head tag:
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-response-targets@2.0.2" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous"></script>
</head>
<body hx-ext="extension-name">
...
\`\`\`
An unminified version is also available at `https://unpkg.com/htmx-ext-extension-name/dist/extension-name.js` (replace `extension-name` with the name of the extension).
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install htmx extensions is to simply copy them into your project. Download the extension from `https://unpkg.com/htmx-ext-extension-name` (replace `extension-name` with the name of the extension) e.g., https://unpkg.com/htmx-ext-response-targets. Then add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install htmx extensions via [npm](https://www.npmjs.com/) (replace `extension-name` with the name of the extension):
\`\`\`shell
npm install htmx-ext-extension-name
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-extension-name/dist/extension-name.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-extension-name` via npm (replace `extension-name` with the name of the extension)
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-extension-name`; // replace `extension-name` with the name of the extension
\`\`\`
Note: [Idiomorph](/extensions/idiomorph) does not follow the naming convention of htmx extensions. Use `idiomorph` instead of `htmx-ext-idiomorph`. For example, `https://unpkg.com/idiomorph` or `npm install idiomorph`.
Note: Community extensions hosted outside this repository might have different installation instructions. Please check the corresponding repository for set-up guidance.
### Enabling Extensions
To enable an extension, add a `hx-ext="extension-name"` attribute to `<body>` or another HTML element (replace `extension-name` with the name of the extension). The extension will be applied to all child elements.
The following example shows how to enable [response-targets](/extensions/response-targets) extension, allowing you to specify different target elements to be swapped based on HTTP response code.
\`\`\`html
<body hx-ext="response-targets">
...
<button hx-post="/register" hx-target="#response-div" hx-target-404="#not-found">
Register!
</button>
<div id="response-div"></div>
<div id="not-found"></div>
...
</body>
\`\`\`
### Creating Extensions
If you are interested in adding your own extension to htmx, please [see the extension docs](/extensions/building).
## Events & Logging {#events}
Htmx has an extensive [events mechanism](@/reference.md#events), which doubles as the logging system.
If you want to register for a given htmx event you can use
\`\`\`js
document.body.addEventListener('htmx:load', function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
\`\`\`
or, if you would prefer, you can use the following htmx helper:
\`\`\`javascript
htmx.on("htmx:load", function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
\`\`\`
The `htmx:load` event is fired every time an element is loaded into the DOM by htmx, and is effectively the equivalent
to the normal `load` event.
Some common uses for htmx events are:
### Initialize A 3rd Party Library With Events {#init_3rd_party_with_events}
Using the `htmx:load` event to initialize content is so common that htmx provides a helper function:
\`\`\`javascript
htmx.onLoad(function(target) {
myJavascriptLib.init(target);
});
\`\`\`
This does the same thing as the first example, but is a little cleaner.
### Configure a Request With Events {#config_request_with_events}
You can handle the [`htmx:configRequest`](@/events.md#htmx:configRequest) event in order to modify an AJAX request before it is issued:
\`\`\`javascript
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the request
evt.detail.headers['Authentication-Token'] = getAuthToken(); // add a new header into the request
});
\`\`\`
Here we add a parameter and header to the request before it is sent.
### Modifying Swapping Behavior With Events {#modifying_swapping_behavior_with_events}
You can handle the [`htmx:beforeSwap`](@/events.md#htmx:beforeSwap) event in order to modify the swap behavior of htmx:
\`\`\`javascript
document.body.addEventListener('htmx:beforeSwap', function(evt) {
if(evt.detail.xhr.status === 404){
// alert the user when a 404 occurs (maybe use a nicer mechanism than alert())
alert("Error: Could Not Find Resource");
} else if(evt.detail.xhr.status === 422){
// allow 422 responses to swap as we are using this as a signal that
// a form was submitted with bad data and want to rerender with the
// errors
//
// set isError to false to avoid error logging in console
evt.detail.shouldSwap = true;
evt.detail.isError = false;
} else if(evt.detail.xhr.status === 418){
// if the response code 418 (I'm a teapot) is returned, retarget the
// content of the response to the element with the id `teapot`
evt.detail.shouldSwap = true;
evt.detail.target = htmx.find("#teapot");
}
});
\`\`\`
Here we handle a few [400-level error response codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses)
that would normally not do a swap in htmx.
### Event Naming {#event_naming}
Note that all events are fired with two different names
* Camel Case
* Kebab Case
So, for example, you can listen for `htmx:afterSwap` or for `htmx:after-swap`. This facilitates interoperability
with other libraries. [Alpine.js](https://github.com/alpinejs/alpine/), for example, requires kebab case.
### Logging
If you set a logger at `htmx.logger`, every event will be logged. This can be very useful for troubleshooting:
\`\`\`javascript
htmx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
\`\`\`
## Debugging
Declarative and event driven programming with htmx (or any other declarative language) can be a wonderful and highly productive
activity, but one disadvantage when compared with imperative approaches is that it can be trickier to debug.
Figuring out why something *isn't* happening, for example, can be difficult if you don't know the tricks.
Well, here are the tricks:
The first debugging tool you can use is the `htmx.logAll()` method. This will log every event that htmx triggers and
will allow you to see exactly what the library is doing.
\`\`\`javascript
htmx.logAll();
\`\`\`
Of course, that won't tell you why htmx *isn't* doing something. You might also not know *what* events a DOM
element is firing to use as a trigger. To address this, you can use the
[`monitorEvents()`](https://developers.google.com/web/updates/2015/05/quickly-monitor-events-from-the-console-panel) method available in the
browser console:
\`\`\`javascript
monitorEvents(htmx.find("#theElement"));
\`\`\`
This will spit out all events that are occurring on the element with the id `theElement` to the console, and allow you
to see exactly what is going on with it.
Note that this *only* works from the console, you cannot embed it in a script tag on your page.
Finally, push come shove, you might want to just debug `htmx.js` by loading up the unminimized version. It's
about 2500 lines of javascript, so not an insurmountable amount of code. You would most likely want to set a break
point in the `issueAjaxRequest()` and `handleAjaxResponse()` methods to see what's going on.
And always feel free to jump on the [Discord](https://htmx.org/discord) if you need help.
### Creating Demos
Sometimes, in order to demonstrate a bug or clarify a usage, it is nice to be able to use a javascript snippet
site like [jsfiddle](https://jsfiddle.net/). To facilitate easy demo creation, htmx hosts a demo script
site that will install:
* htmx
* hyperscript
* a request mocking library
Simply add the following script tag to your demo/fiddle/whatever:
\`\`\`html
<script src="https://demo.htmx.org"></script>
\`\`\`
This helper allows you to add mock responses by adding `template` tags with a `url` attribute to indicate which URL.
The response for that url will be the innerHTML of the template, making it easy to construct mock responses. You can
add a delay to the response with a `delay` attribute, which should be an integer indicating the number of milliseconds
to delay
You may embed simple expressions in the template with the `${}` syntax.
Note that this should only be used for demos and is in no way guaranteed to work for long periods of time
as it will always be grabbing the latest versions htmx and hyperscript!
#### Demo Example
Here is an example of the code in action:
\`\`\`html
<!-- load demo environment -->
<script src="https://demo.htmx.org"></script>
<!-- post to /foo -->
<button hx-post="/foo" hx-target="#result">
Count Up
</button>
<output id="result"></output>
<!-- respond to /foo with some dynamic content in a template tag -->
<script>
globalInt = 0;
</script>
<template url="/foo" delay="500"> <!-- note the url and delay attributes -->
${globalInt++}
</template>
\`\`\`
## Scripting {#scripting}
While htmx encourages a hypermedia approach to building web applications, it offers many options for client scripting. Scripting is included in the REST-ful description of web architecture, see: [Code-On-Demand](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_7). As much as is feasible, we recommend a [hypermedia-friendly](/essays/hypermedia-friendly-scripting) approach to scripting in your web application:
* [Respect HATEOAS](/essays/hypermedia-friendly-scripting#prime_directive)
* [Use events to communicate between components](/essays/hypermedia-friendly-scripting#events)
* [Use islands to isolate non-hypermedia components from the rest of your application](/essays/hypermedia-friendly-scripting#islands)
* [Consider inline scripting](/essays/hypermedia-friendly-scripting#inline)
The primary integration point between htmx and scripting solutions is the [events](#events) that htmx sends and can
respond to. See the SortableJS example in the [3rd Party Javascript](#3rd-party) section for a good template for
integrating a JavaScript library with htmx via events.
Scripting solutions that pair well with htmx include:
* [VanillaJS](http://vanilla-js.com/) - Simply using the built-in abilities of JavaScript to hook in event handlers to
respond to the events htmx emits can work very well for scripting. This is an extremely lightweight and increasingly
popular approach.
* [AlpineJS](https://alpinejs.dev/) - Alpine.js provides a rich set of tools for creating sophisticated front end scripts,
including reactive programming support, while still remaining extremely lightweight. Alpine encourages the "inline scripting"
approach that we feel pairs well with htmx.
* [jQuery](https://jquery.com/) - Despite its age and reputation in some circles, jQuery pairs very well with htmx, particularly
in older code-bases that already have a lot of jQuery in them.
* [hyperscript](https://hyperscript.org) - Hyperscript is an experimental front-end scripting language created by the same
team that created htmx. It is designed to embed well in HTML and both respond to and create events, and pairs very well
with htmx.
We have an entire chapter entitled ["Client-Side Scripting"](https://hypermedia.systems/client-side-scripting/) in [our
book](https://hypermedia.systems) that looks at how scripting can be integrated into your htmx-based application.
### <a name="hx-on"></a>[The `hx-on*` Attributes](#hx-on)
HTML allows the embedding of inline scripts via the [`onevent` properties](https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers#using_onevent_properties),
such as `onClick`:
\`\`\`html
<button onclick="alert('You clicked me!')">
Click Me!
</button>
\`\`\`
This feature allows scripting logic to be co-located with the HTML elements the logic applies to, giving good
[Locality of Behaviour (LoB)](/essays/locality-of-behaviour). Unfortunately, HTML only allows `on*` attributes for a fixed
number of [specific DOM events](https://www.w3schools.com/tags/ref_eventattributes.asp) (e.g. `onclick`) and
doesn't provide a generalized mechanism for responding to arbitrary events on elements.
In order to address this shortcoming, htmx offers [`hx-on*`](/attributes/hx-on) attributes. These attributes allow
you to respond to any event in a manner that preserves the LoB of the standard `on*` properties.
If we wanted to respond to the `click` event using an `hx-on` attribute, we would write this:
\`\`\`html
<button hx-on:click="alert('You clicked me!')">
Click Me!
</button>
\`\`\`
So, the string `hx-on`, followed by a colon (or a dash), then by the name of the event.
For a `click` event, of course, we would recommend sticking with the standard `onclick` attribute. However, consider an
htmx-powered button that wishes to add a parameter to a request using the `htmx:config-request` event. This would not
be possible using a standard `on*` property, but it can be done using the `hx-on:htmx:config-request` attribute:
\`\`\`html
<button hx-post="/example"
hx-on:htmx:config-request="event.detail.parameters.example = 'Hello Scripting!'">
Post Me!
</button>
\`\`\`
Here the `example` parameter is added to the `POST` request before it is issued, with the value 'Hello Scripting!'.
Another usecase is to [reset user input](@/examples/reset-user-input.md) on successful requests using the `afterRequest`
event, avoiding the need for something like an out of band swap.
The `hx-on*` attributes are a very simple mechanism for generalized embedded scripting. It is _not_ a replacement for more
fully developed front-end scripting solutions such as AlpineJS or hyperscript. It can, however, augment a VanillaJS-based
approach to scripting in your htmx-powered application.
Note that HTML attributes are *case insensitive*. This means that, unfortunately, events that rely on capitalization/
camel casing, cannot be responded to. If you need to support camel case events we recommend using a more fully
functional scripting solution such as AlpineJS or hyperscript. htmx dispatches all its events in both camelCase and in
kebab-case for this very reason.
### 3rd Party Javascript {#3rd-party}
Htmx integrates fairly well with third party libraries. If the library fires events on the DOM, you can use those events to
trigger requests from htmx.
A good example of this is the [SortableJS demo](@/examples/sortable.md):
\`\`\`html
<form class="sortable" hx-post="/items" hx-trigger="end">
<div class="htmx-indicator">Updating...</div>
<div><input type='hidden' name='item' value='1'/>Item 1</div>
<div><input type='hidden' name='item' value='2'/>Item 2</div>
<div><input type='hidden' name='item' value='2'/>Item 3</div>
</form>
\`\`\`
With Sortable, as with most javascript libraries, you need to initialize content at some point.
In jquery you might do this like so:
\`\`\`javascript
$(document).ready(function() {
var sortables = document.body.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
});
\`\`\`
In htmx, you would instead use the `htmx.onLoad` function, and you would select only from the newly loaded content,
rather than the entire document:
\`\`\`js
htmx.onLoad(function(content) {
var sortables = content.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
})
\`\`\`
This will ensure that as new content is added to the DOM by htmx, sortable elements are properly initialized.
If javascript adds content to the DOM that has htmx attributes on it, you need to make sure that this content
is initialized with the `htmx.process()` function.
For example, if you were to fetch some data and put it into a div using the `fetch` API, and that HTML had
htmx attributes in it, you would need to add a call to `htmx.process()` like this:
\`\`\`js
let myDiv = document.getElementById('my-div')
fetch('http://example.com/movies.json')
.then(response => response.text())
.then(data => { myDiv.innerHTML = data; htmx.process(myDiv); } );
\`\`\`
Some 3rd party libraries create content from HTML template elements. For instance, Alpine JS uses the `x-if`
attribute on templates to add content conditionally. Such templates are not initially part of the DOM and,
if they contain htmx attributes, will need a call to `htmx.process()` after they are loaded. The following
example uses Alpine's `$watch` function to look for a change of value that would trigger conditional content:
\`\`\`html
<div x-data="{show_new: false}"
x-init="$watch('show_new', value => {
if (show_new) {
htmx.process(document.querySelector('#new_content'))
}
})">
<button @click = "show_new = !show_new">Toggle New Content</button>
<template x-if="show_new">
<div id="new_content">
<a hx-get="/server/newstuff" href="#">New Clickable</a>
</div>
</template>
</div>
\`\`\`
#### Web Components {#web-components}
Please see the [Web Components Examples](@/examples/web-components.md) page for examples on how to integrate htmx
with web components.
## Caching
htmx works with standard [HTTP caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching)
mechanisms out of the box.
If your server adds the
[`Last-Modified`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified)
HTTP response header to the response for a given URL, the browser will automatically add the
[`If-Modified-Since`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since)
request HTTP header to the next requests to the same URL. Be mindful that if
your server can render different content for the same URL depending on some other
headers, you need to use the [`Vary`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#vary)
response HTTP header. For example, if your server renders the full HTML when the
`HX-Request` header is missing or `false`, and it renders a fragment of that HTML
when `HX-Request: true`, you need to add `Vary: HX-Request`. That causes the cache to be
keyed based on a composite of the response URL and the `HX-Request` request header —
rather than being based just on the response URL.
If you are unable (or unwilling) to use the `Vary` header, you can alternatively set the configuration parameter
`getCacheBusterParam` to `true`. If this configuration variable is set, htmx will include a cache-busting parameter
in `GET` requests that it makes, which will prevent browsers from caching htmx-based and non-htmx based responses
in the same cache slot.
htmx also works with [`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag)
as expected. Be mindful that if your server can render different content for the same
URL (for example, depending on the value of the `HX-Request` header), the server needs
to generate a different `ETag` for each content.
## Security
htmx allows you to define logic directly in your DOM. This has a number of advantages, the largest being
[Locality of Behavior](@/essays/locality-of-behaviour.md), which makes your system easier to understand and
maintain.
A concern with this approach, however, is security: since htmx increases the expressiveness of HTML, if a malicious
user is able to inject HTML into your application, they can leverage this expressiveness of htmx to malicious
ends.
### Rule 1: Escape All User Content
The first rule of HTML-based web development has always been: *do not trust input from the user*. You should escape all
3rd party, untrusted content that is injected into your site. This is to prevent, among other issues,
[XSS attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
There is extensive documentation on XSS and how to prevent it on the excellent [OWASP Website](https://owasp.org/www-community/attacks/xss/),
including a [Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).
The good news is that this is a very old and well understood topic, and the vast majority of server-side templating languages
support [automatic escaping](https://docs.djangoproject.com/en/4.2/ref/templates/language/#automatic-html-escaping) of
content to prevent just such an issue.
That being said, there are times people choose to inject HTML more dangerously, often via some sort of `raw()`
mechanism in their templating language. This can be done for good reasons, but if the content being injected is coming
from a 3rd party then it _must_ be scrubbed, including removing attributes starting with `hx-` and `data-hx`, as well as
inline `<script>` tags, etc.
If you are injecting raw HTML and doing your own escaping, a best practice is to *whitelist* the attributes and tags you
allow, rather than to blacklist the ones you disallow.
### htmx Security Tools
Of course, bugs happen and developers are not perfect, so it is good to have a layered approach to security for
your web application, and htmx provides tools to help secure your application as well.
Let's take a look at them.
#### `hx-disable`
The first tool htmx provides to help further secure your application is the [`hx-disable`](/attributes/hx-disable)
attribute. This attribute will prevent processing of all htmx attributes on a given element, and on all elements within
it. So, for example, if you were including raw HTML content in a template (again, this is not recommended!) then you
could place a div around the content with the `hx-disable` attribute on it:
\`\`\`html
<div hx-disable>
<%= raw(user_content) %>
</div>
\`\`\`
And htmx will not process any htmx-related attributes or features found in that content. This attribute cannot be
disabled by injecting further content: if an `hx-disable` attribute is found anywhere in the parent hierarchy of an
element, it will not be processed by htmx.
#### `hx-history`
Another security consideration is htmx history cache. You may have pages that have sensitive data that you do not
want stored in the users `localStorage` cache. You can omit a given page from the history cache by including the
[`hx-history`](/attributes/hx-history) attribute anywhere on the page, and setting its value to `false`.
#### Configuration Options
htmx also provides configuration options related to security:
* `htmx.config.selfRequestsOnly` - if set to `true`, only requests to the same domain as the current document will be allowed
* `htmx.config.allowScriptTags` - htmx will process `<script>` tags found in new content it loads. If you wish to disable
this behavior you can set this configuration variable to `false`
* `htmx.config.historyCacheSize` - can be set to `0` to avoid storing any HTML in the `localStorage` cache
* `htmx.config.allowEval` - can be set to `false` to disable all features of htmx that rely on eval:
* event filters
* `hx-on:` attributes
* `hx-vals` with the `js:` prefix
* `hx-headers` with the `js:` prefix
Note that all features removed by disabling `eval()` can be reimplemented using your own custom javascript and the
htmx event model.
#### Events
If you want to allow requests to some domains beyond the current host, but not leave things totally open, you can
use the `htmx:validateUrl` event. This event will have the request URL available in the `detail.url` slot, as well
as a `sameHost` property.
You can inspect these values and, if the request is not valid, invoke `preventDefault()` on the event to prevent the
request from being issued.
\`\`\`javascript
document.body.addEventListener('htmx:validateUrl', function (evt) {
// only allow requests to the current server as well as myserver.com
if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
evt.preventDefault();
}
});
\`\`\`
### CSP Options
Browsers also provide tools for further securing your web application. The most powerful tool available is a
[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). Using a CSP you can tell the
browser to, for example, not issue requests to non-origin hosts, to not evaluate inline script tags, etc.
Here is an example CSP in a `meta` tag:
\`\`\`html
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
\`\`\`
This tells the browser "Only allow connections to the original (source) domain". This would be redundant with the
`htmx.config.selfRequestsOnly`, but a layered approach to security is warranted and, in fact, ideal, when dealing
with application security.
A full discussion of CSPs is beyond the scope of this document, but the [MDN Article](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) provide a good jumping off point
for exploring this topic.
### CSRF Prevention
The assignment and checking of CSRF tokens are typically backend responsibilities, but `htmx` can support returning the CSRF token automatically with every request using the `hx-headers` attribute. The attribute needs to be added to the element issuing the request or one of its ancestor elements. This makes the `html` and `body` elements effective global vehicles for adding the CSRF token to the `HTTP` request header, as illustrated below.
\`\`\`html
<html lang="en" hx-headers='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
:
</html>
\`\`\`
\`\`\`html
<body hx-headers='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
:
</body>
\`\`\`
The above elements are usually unique in an HTML document and should be easy to locate within templates.
## Configuring htmx {#config}
Htmx has some configuration options that can be accessed either programmatically or declaratively. They are
listed below:
<div class="info-table">
| Config Variable | Info |
|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `htmx.config.historyEnabled` | defaults to `true`, really only useful for testing |
| `htmx.config.historyCacheSize` | defaults to 10 |
| `htmx.config.refreshOnHistoryMiss` | defaults to `false`, if set to `true` htmx will issue a full page refresh on history misses rather than use an AJAX request |
| `htmx.config.defaultSwapStyle` | defaults to `innerHTML` |
| `htmx.config.defaultSwapDelay` | defaults to 0 |
| `htmx.config.defaultSettleDelay` | defaults to 20 |
| `htmx.config.includeIndicatorStyles` | defaults to `true` (determines if the indicator styles are loaded) |
| `htmx.config.indicatorClass` | defaults to `htmx-indicator` |
| `htmx.config.requestClass` | defaults to `htmx-request` |
| `htmx.config.addedClass` | defaults to `htmx-added` |
| `htmx.config.settlingClass` | defaults to `htmx-settling` |
| `htmx.config.swappingClass` | defaults to `htmx-swapping` |
| `htmx.config.allowEval` | defaults to `true`, can be used to disable htmx's use of eval for certain features (e.g. trigger filters) |
| `htmx.config.allowScriptTags` | defaults to `true`, determines if htmx will process script tags found in new content |
| `htmx.config.inlineScriptNonce` | defaults to `''`, meaning that no nonce will be added to inline scripts |
| `htmx.config.attributesToSettle` | defaults to `["class", "style", "width", "height"]`, the attributes to settle during the settling phase |
| `htmx.config.inlineStyleNonce` | defaults to `''`, meaning that no nonce will be added to inline styles |
| `htmx.config.useTemplateFragments` | defaults to `false`, HTML template tags for parsing content from the server (not IE11 compatible!) |
| `htmx.config.wsReconnectDelay` | defaults to `full-jitter` |
| `htmx.config.wsBinaryType` | defaults to `blob`, the [type of binary data](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) being received over the WebSocket connection |
| `htmx.config.disableSelector` | defaults to `[hx-disable], [data-hx-disable]`, htmx will not process elements with this attribute on it or a parent |
| `htmx.config.withCredentials` | defaults to `false`, allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates |
| `htmx.config.timeout` | defaults to 0, the number of milliseconds a request can take before automatically being terminated |
| `htmx.config.scrollBehavior` | defaults to 'instant', the scroll behavior when using the [show](@/attributes/hx-swap.md#scrolling-scroll-show) modifier with `hx-swap`. The allowed values are `instant` (scrolling should happen instantly in a single jump), `smooth` (scrolling should animate smoothly) and `auto` (scroll behavior is determined by the computed value of [scroll-behavior](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior)). |
| `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier. |
| `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will append the target element to the `GET` request in the format `org.htmx.cache-buster=targetElementId` |
| `htmx.config.globalViewTransitions` | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content. |
| `htmx.config.methodsThatUseUrlParams` | defaults to `["get", "delete"]`, htmx will format requests with these methods by encoding their parameters in the URL, not the request body |
| `htmx.config.selfRequestsOnly` | defaults to `true`, whether to only allow AJAX requests to the same domain as the current document |
| `htmx.config.ignoreTitle` | defaults to `false`, if set to `true` htmx will not update the title of the document when a `title` tag is found in new content |
| `htmx.config.disableInheritance` | disables attribute inheritance in htmx, which can then be overridden by the [`hx-inherit`](@/attributes/hx-inherit.md) attribute |
| `htmx.config.scrollIntoViewOnBoost` | defaults to `true`, whether or not the target of a boosted element is scrolled into the viewport. If `hx-target` is omitted on a boosted element, the target defaults to `body`, causing the page to scroll to the top. |
| `htmx.config.triggerSpecsCache` | defaults to `null`, the cache to store evaluated trigger specifications into, improving parsing performance at the cost of more memory usage. You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) |
| `htmx.config.responseHandling` | the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error |
| `htmx.config.allowNestedOobSwaps` | defaults to `true`, whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps). |
</div>
You can set them directly in javascript, or you can use a `meta` tag:
\`\`\`html
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
\`\`\`
## Conclusion
And that's it!
Have fun with htmx! You can accomplish [quite a bit](@/examples/_index.md) without writing a lot of code!
</div>
</div>
```
# events.md
```md
+++
title = "Events"
+++
Htmx provides an extensive events system that can be used to modify and enhance behavior. Events
are listed below.
### Event - `htmx:abort` {#htmx:abort}
This event is different than other events: htmx does not *trigger* it, but rather *listens* for it.
If you send an `htmx:abort` event to an element making a request, it will abort the request:
\`\`\`html
<button id="request-button" hx-post="/example">Issue Request</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">Cancel Request</button>
\`\`\`
### Event - `htmx:afterOnLoad` {#htmx:afterOnLoad}
This event is triggered after an AJAX `onload` has finished. Note that this does not mean that the content
has been swapped or settled yet, only that the request has finished.
##### Details
* `detail.elt` - the element that dispatched the request or if the body no longer contains the element then the closest parent
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:afterProcessNode` {#htmx:afterProcessNode}
This event is triggered after htmx has initialized a DOM node. It can be useful for extensions to build additional features onto a node.
##### Details
* `detail.elt` - the element being initialized
### Event - `htmx:afterRequest` {#htmx:afterRequest}
This event is triggered after an AJAX request has finished either in the case of a successful request (although
one that may have returned a remote error code such as a `404`) or in a network error situation. This event
can be paired with [`htmx:beforeRequest`](#htmx:beforeRequest) to wrap behavior around a request cycle.
##### Details
* `detail.elt` - the element that dispatched the request or if the body no longer contains the element then the closest parent
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
* `detail.successful` - true if the response has a 20x status code or is marked `detail.isError = false` in the
`htmx:beforeSwap` event, else false
* `detail.failed` - true if the response does not have a 20x status code or is marked `detail.isError = true` in the
`htmx:beforeSwap` event, else false
### Event - `htmx:afterSettle` {#htmx:afterSettle}
This event is triggered after the DOM has [settled](@/docs.md#request-operations).
##### Details
* `detail.elt` - the updated element
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:afterSwap` {#htmx:afterSwap}
This event is triggered after new content has been [swapped into the DOM](@/docs.md#swapping).
##### Details
* `detail.elt` - the swapped in element
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeCleanupElement` {#htmx:beforeCleanupElement}
This event is triggered before htmx [disables](@/attributes/hx-disable.md) an element or removes it from the DOM.
##### Details
* `detail.elt` - the element to be cleaned up
### Event - `htmx:beforeOnLoad` {#htmx:beforeOnLoad}
This event is triggered before any response processing occurs. If you call `preventDefault()` on the event to cancel it, no swap will occur.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeProcessNode` {#htmx:beforeProcessNode}
This event is triggered before htmx initializes a DOM node and has processed all of its `hx-` attributes. This gives extensions and other external code the ability to modify the contents of a DOM node before it is processed.
##### Details
* `detail.elt` - the element being initialized
### Event - `htmx:beforeRequest` {#htmx:beforeRequest}
This event is triggered before an AJAX request is issued. If you call `preventDefault()` on the event to cancel it, no request will occur.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.boosted` - true if the request is via an element using boosting
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeSend` {#htmx:beforeSend}
This event is triggered right before a request is sent. You may not cancel the request with this event.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeSwap` {#htmx:beforeSwap}
This event is triggered before any new content has been [swapped into the DOM](@/docs.md#swapping).
Most values on `detail` can be set to override subsequent behavior, other than where response headers take precedence.
If you call `preventDefault()` on the event to cancel it, no swap will occur.
You can modify the default swap behavior by modifying the `shouldSwap`, `selectOverride`, `swapOverride` and `target` properties of the event detail.
See the documentation on [configuring swapping](@/docs.md#modifying_swapping_behavior_with_events) for more details.
##### Details
* `detail.elt` - the target of the swap
* `detail.xhr` - the `XMLHttpRequest`
* `detail.boosted` - true if the request is via an element using boosting
* `detail.requestConfig` - the configuration of the AJAX request
* `detail.requestConfig.elt` - the element that dispatched the request
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
* `detail.ignoreTitle` - if `true` any title tag in the response will be ignored
* `detail.isError` - whether error events should be triggered and also determines the values of `detail.successful` and `detail.failed` in later events
* `detail.serverResponse` - the server response as text to be used for the swap
* `detail.selectOverride` - add this to use instead of an [`hx-select`](@/attributes/hx-select.md) value
* `detail.swapOverride` - add this to use instead of an [`hx-swap`](@/attributes/hx-swap.md) value
* `detail.target` - the target of the swap
### Event - `htmx:beforeTransition` {#htmx:beforeTransition}
This event is triggered before a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
wrapped swap occurs. If you call `preventDefault()` on the event to cancel it, the View Transition will not occur and the normal swapping logic will
happen instead.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.boosted` - true if the request is via an element using boosting
* `detail.requestConfig` - the configuration of the AJAX request
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
* `detail.target` - the target of the swap
### Event - `htmx:configRequest` {#htmx:configRequest}
This event is triggered after htmx has collected parameters for inclusion in the request. It can be
used to include or update the parameters that htmx will send:
\`\`\`javascript
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the mix
});
\`\`\`
Note that if an input value appears more than once the value in the `parameters` object will be an array, rather
than a single value.
##### Details
* `detail.parameters` - the parameters that will be submitted in the request
* `detail.unfilteredParameters` - the parameters that were found before filtering by [`hx-params`](@/attributes/hx-params.md)
* `detail.headers` - the request headers
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.verb` - the HTTP verb in use
### Event - `htmx:confirm` {#htmx:confirm}
This event is fired on every trigger for a request (not just on elements that have a hx-confirm attribute).
It allows you to cancel (or delay) issuing the AJAX request.
If you call `preventDefault()` on the event, it will not issue the given request.
The `detail` object contains a function, `evt.detail.issueRequest(skipConfirmation=false)`, that can be used to issue the actual AJAX request at a later point.
Combining these two features allows you to create an asynchronous confirmation dialog.
Here is a basic example that shows the basic usage of the `htmx:confirm` event without altering the default behavior:
\`\`\`javascript
document.body.addEventListener('htmx:confirm', function(evt) {
// 0. To modify the behavior only for elements with the hx-confirm attribute,
// check if evt.detail.target.hasAttribute('hx-confirm')
// 1. Prevent the default behavior (this will prevent the request from being issued)
evt.preventDefault();
// 2. Do your own logic here
console.log(evt.detail)
// 3. Manually issue the request when you are ready
evt.detail.issueRequest(); // or evt.detail.issueRequest(true) to skip the built-in window.confirm()
});
\`\`\`
And here is an example using [sweet alert](https://sweetalert.js.org/guides/) on any element with a `confirm-with-sweet-alert="{question}"` attribute on it:
\`\`\`javascript
document.body.addEventListener('htmx:confirm', function(evt) {
// 1. The requirement to show the sweet alert is that the element has a confirm-with-sweet-alert
// attribute on it, if it doesn't we can return early and let the default behavior happen
if (!evt.detail.target.hasAttribute('confirm-with-sweet-alert')) return
// 2. Get the question from the attribute
const question = evt.detail.target.getAttribute('confirm-with-sweet-alert');
// 3. Prevent the default behavior (this will prevent the request from being issued)
evt.preventDefault();
// 4. Show the sweet alert
swal({
title: "Are you sure?",
text: question || "Are you sure you want to continue?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
// 5. If the user confirms, we can manually issue the request
evt.detail.issueRequest(true); // true to skip the built-in window.confirm()
}
});
});
\`\`\`
##### Details
* `detail.elt` - the element in question
* `detail.etc` - additional request information (mostly unused)
* `detail.issueRequest(skipConfirmation=false)` - a function that can be invoked to issue the request (should be paired with `evt.preventDefault()`!), if skipConfirmation is `true` the original `window.confirm()` is not executed
* `detail.path` - the path of the request
* `detail.target` - the element that triggered the request
* `detail.triggeringEvent` - the original event that triggered this request
* `detail.verb` - the verb of the request (e.g. `GET`)
* `detail.question` - the question passed to `hx-confirm` attribute (only available if `hx-confirm` attribute is present)
### Event - `htmx:historyCacheError` {#htmx:historyCacheError}
This event is triggered when an attempt to save the cache to `localStorage` fails
##### Details
* `detail.cause` - the `Exception` that was thrown when attempting to save history to `localStorage`
### Event - `htmx:historyCacheMiss` {#htmx:historyCacheMiss}
This event is triggered when a cache miss occurs when restoring history
##### Details
* `detail.xhr` - the `XMLHttpRequest` that will retrieve the remote content for restoration
* `detail.path` - the path and query of the page being restored
### Event - `htmx:historyCacheMissError` {#htmx:historyCacheMissError}
This event is triggered when a cache miss occurs and a response has been retrieved from the server
for the content to restore, but the response is an error (e.g. `404`)
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.path` - the path and query of the page being restored
### Event - `htmx:historyCacheMissLoad` {#htmx:historyCacheMissLoad}
This event is triggered when a cache miss occurs and a response has been retrieved successfully from the server
for the content to restore
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.path` - the path and query of the page being restored
### Event - `htmx:historyRestore` {#htmx:historyRestore}
This event is triggered when htmx handles a history restoration action
##### Details
* `detail.path` - the path and query of the page being restored
### Event - `htmx:beforeHistorySave` {#htmx:beforeHistorySave}
This event is triggered before the content is saved in the history api.
##### Details
* `detail.path` - the path and query of the page being restored
* `detail.historyElt` - the history element being restored into
### Event - `htmx:load` {#htmx:load}
This event is triggered when a new node is loaded into the DOM by htmx.
##### Details
* `detail.elt` - the newly added element
### Event - `htmx:noSSESourceError` {#htmx:noSSESourceError}
This event is triggered when an element refers to an SSE event in its trigger, but no parent SSE source has been defined
##### Details
* `detail.elt` - the element with the bad SSE trigger
### Event - `htmx:oobAfterSwap` {#htmx:oobAfterSwap}
This event is triggered as part of an [out of band swap](@/docs.md#oob_swaps) and behaves identically to an [after swap event](#htmx:afterSwap)
##### Details
* `detail.elt` - the swapped in element
* `detail.shouldSwap` - if the content will be swapped (defaults to `true`)
* `detail.target` - the target of the swap
* `detail.fragment` - the response fragment
### Event - `htmx:oobBeforeSwap` {#htmx:oobBeforeSwap}
This event is triggered as part of an [out of band swap](@/docs.md#oob_swaps) and behaves identically to a [before swap event](#htmx:beforeSwap)
##### Details
* `detail.elt` - the target of the swap
* `detail.shouldSwap` - if the content will be swapped (defaults to `true`)
* `detail.target` - the target of the swap
* `detail.fragment` - the response fragment
### Event - `htmx:oobErrorNoTarget` {#htmx:oobErrorNoTarget}
This event is triggered when an [out of band swap](@/docs.md#oob_swaps) does not have a corresponding element
in the DOM to switch with.
##### Details
* `detail.content` - the element with the bad oob `id`
### Event - `htmx:onLoadError` {#htmx:onLoadError}
This event is triggered when an error occurs during the `load` handling of an AJAX call
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.exception` - the exception that occurred
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:prompt` {#htmx:prompt}
This event is triggered after a prompt has been shown to the user with the [`hx-prompt`](@/attributes/hx-prompt.md)
attribute. If this event is cancelled, the AJAX request will not occur.
##### Details
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.prompt` - the user response to the prompt
### Event - `htmx:beforeHistoryUpdate` {#htmx:beforeHistoryUpdate}
This event is triggered before a history update is performed. It can be
used to modify the `path` or `type` used to update the history.
##### Details
* `detail.history` - the `path` and `type` (push, replace) for the history update
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:pushedIntoHistory` {#htmx:pushedIntoHistory}
This event is triggered after a URL has been pushed into history.
##### Details
* `detail.path` - the path and query of the URL that has been pushed into history
### Event - `htmx:replacedInHistory` {#htmx:replacedInHistory}
This event is triggered after a URL has been replaced in history.
##### Details
* `detail.path` - the path and query of the URL that has been replaced in history
### Event - `htmx:responseError` {#htmx:responseError}
This event is triggered when an HTTP error response occurs
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:sendAbort` {#htmx:sendAbort}
This event is triggered when a request is aborted
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:sendError` {#htmx:sendError}
This event is triggered when a network error prevents an HTTP request from occurring
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:sseError` {#htmx:sseError}
This event is triggered when an error occurs with an SSE source
##### Details
* `detail.elt` - the element with the bad SSE source
* `detail.error` - the error
* `detail.source` - the SSE source
### Event - `htmx:swapError` {#htmx:swapError}
This event is triggered when an error occurs during the swap phase
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:targetError` {#htmx:targetError}
This event is triggered when a bad selector is used for a [`hx-target`](@/attributes/hx-target.md) attribute (e.g. an
element ID without a preceding `#`)
##### Details
* `detail.elt` - the element that triggered the request
* `detail.target` - the bad CSS selector
### Event - `htmx:timeout` {#htmx:timeout}
This event is triggered when a request timeout occurs. This wraps the typical `timeout` event of XMLHttpRequest.
Timeout time can be set using `htmx.config.timeout` or per element using [`hx-request`](@/attributes/hx-request.md)
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:trigger` {#htmx:trigger}
This event is triggered whenever an AJAX request would be, even if no AJAX request is specified. It
is primarily intended to allow `hx-trigger` to execute client-side scripts; AJAX requests have more
granular events available, like [`htmx:beforeRequest`](#htmx:beforeRequest) or [`htmx:afterRequest`](#htmx:afterRequest).
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:validateUrl` {#htmx:validateUrl}
This event is triggered before a request is made, allowing you to validate the URL that htmx is going to request. If
`preventDefault()` is invoked on the event, the request will not be made.
\`\`\`javascript
document.body.addEventListener('htmx:validateUrl', function (evt) {
// only allow requests to the current server as well as myserver.com
if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
evt.preventDefault();
}
});
\`\`\`
##### Details
* `detail.elt` - the element that triggered the request
* `detail.url` - the URL Object representing the URL that a request will be sent to.
* `detail.sameHost` - will be `true` if the request is to the same host as the document
### Event - `htmx:validation:validate` {#htmx:validation:validate}
This event is triggered before an element is validated. It can be used with the `elt.setCustomValidity()` method
to implement custom validation rules.
\`\`\`html
<form hx-post="/test">
<input _="on htmx:validation:validate
if my.value != 'foo'
call me.setCustomValidity('Please enter the value foo')
else
call me.setCustomValidity('')"
name="example">
</form>
\`\`\`
##### Details
* `detail.elt` - the element to be validated
### Event - `htmx:validation:failed` {#htmx:validation:failed}
This event is triggered when an element fails validation.
##### Details
* `detail.elt` - the element that failed validation
* `detail.message` - the validation error message
* `detail.validity` - the validity object, which contains properties specifying how validation failed
### Event - `htmx:validation:halted` {#htmx:validation:halted}
This event is triggered when a request is halted due to validation errors.
##### Details
* `detail.elt` - the element that triggered the request
* `detail.errors` - an array of error objects with the invalid elements and errors associated with them
### Event - `htmx:xhr:abort` {#htmx:xhr:abort}
This event is triggered when an ajax request aborts
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:xhr:loadstart` {#htmx:xhr:loadstart}
This event is triggered when an ajax request starts
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:xhr:loadend` {#htmx:xhr:loadend}
This event is triggered when an ajax request finishes
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:xhr:progress` {#htmx:xhr:progress}
This event is triggered periodically when an ajax request that supports progress is in flight
##### Details
* `detail.elt` - the element that triggered the request
```
# examples/active-search.md
```md
+++
title = "Active Search"
template = "demo.html"
+++
This example actively searches a contacts database as the user enters text.
We start with a search input and an empty table:
\`\`\`html
<h3>
Search Contacts
<span class="htmx-indicator">
<img src="/img/bars.svg"/> Searching...
</span>
</h3>
<input class="form-control" type="search"
name="search" placeholder="Begin Typing To Search Users..."
hx-post="/search"
hx-trigger="input changed delay:500ms, keyup[key=='Enter'], load"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody id="search-results">
</tbody>
</table>
\`\`\`
The input issues a `POST` to `/search` on the [`input`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) event and sets the body of the table to be the resulting content.
We add the `delay:500ms` modifier to the trigger to delay sending the query until the user stops typing. Additionally,
we add the `changed` modifier to the trigger to ensure we don't send new queries when the user doesn't change the
value of the input (e.g. they hit an arrow key, or pasted the same value).
We can use multiple triggers by separating them with a comma, this way we add 2 more triggers:
- `keyup[key=='Enter']` triggers once enter is pressed. We use [event filters](/attributes/hx-trigger#standard-event-filters) here to check for [the key property in the KeyboardEvent object](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key).
- `load` in order to show all results initially on load.
Finally, we show an indicator when the search is in flight with the `hx-indicator` attribute.
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/init", function(request, params){
return searchUI();
});
onPost(/\/search.*/, function(request, params){
var search = params['search'];
var contacts = dataStore.findContactsMatching(search);
return resultsUI(contacts);
});
// templates
function searchUI() {
return ` <h3>
Search Contacts
<span class="htmx-indicator">
<img src="/img/bars.svg"/> Searching...
</span>
</h3>
<input class="form-control" type="search"
name="search" placeholder="Begin Typing To Search Users..."
hx-post="/search"
hx-trigger="input changed delay:500ms, keyup[key=='Enter'], load"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody id="search-results">
</tbody>
</table>`;
}
function resultsUI(contacts){
var txt = "";
for (var i = 0; i < contacts.length; i++) {
var c = contacts[i];
txt += "<tr><td>" + c.FirstName + "</td><td>" + c.LastName + "</td><td>" + c.Email + "</td></tr>\n";
}
return txt;
}
//datastore
var dataStore = function(){
var data = [
{ "FirstName": "Venus", "LastName": "Grimes", "Email": "lectus.rutrum@Duisa.edu", "City": "Ankara" },
{ "FirstName": "Fletcher", "LastName": "Owen", "Email": "metus@Aenean.org", "City": "Niort" },
{ "FirstName": "William", "LastName": "Hale", "Email": "eu.dolor@risusodio.edu", "City": "Te Awamutu" },
{ "FirstName": "TaShya", "LastName": "Cash", "Email": "tincidunt.orci.quis@nuncnullavulputate.co.uk", "City": "Titagarh" },
{ "FirstName": "Kevyn", "LastName": "Hoover", "Email": "tristique.pellentesque.tellus@Cumsociis.co.uk", "City": "Cuenca" },
{ "FirstName": "Jakeem", "LastName": "Walker", "Email": "Morbi.vehicula.Pellentesque@faucibusorci.org", "City": "St. Andrä" },
{ "FirstName": "Malcolm", "LastName": "Trujillo", "Email": "sagittis@velit.edu", "City": "Fort Resolution" },
{ "FirstName": "Wynne", "LastName": "Rice", "Email": "augue.id@felisorciadipiscing.edu", "City": "Kinross" },
{ "FirstName": "Evangeline", "LastName": "Klein", "Email": "adipiscing.lobortis@sem.org", "City": "San Giovanni in Galdo" },
{ "FirstName": "Jennifer", "LastName": "Russell", "Email": "sapien.Aenean.massa@risus.com", "City": "Laives/Leifers" },
{ "FirstName": "Rama", "LastName": "Freeman", "Email": "Proin@quamPellentesquehabitant.net", "City": "Flin Flon" },
{ "FirstName": "Jena", "LastName": "Mathis", "Email": "non.cursus.non@Phaselluselit.com", "City": "Fort Simpson" },
{ "FirstName": "Alexandra", "LastName": "Maynard", "Email": "porta.elit.a@anequeNullam.ca", "City": "Nazilli" },
{ "FirstName": "Tallulah", "LastName": "Haley", "Email": "ligula@id.net", "City": "Bay Roberts" },
{ "FirstName": "Timon", "LastName": "Small", "Email": "velit.Quisque.varius@gravidaPraesent.org", "City": "Girona" },
{ "FirstName": "Randall", "LastName": "Pena", "Email": "facilisis@Donecconsectetuer.edu", "City": "Edam" },
{ "FirstName": "Conan", "LastName": "Vaughan", "Email": "luctus.sit@Classaptenttaciti.edu", "City": "Nadiad" },
{ "FirstName": "Dora", "LastName": "Allen", "Email": "est.arcu.ac@Vestibulumante.co.uk", "City": "Renfrew" },
{ "FirstName": "Aiko", "LastName": "Little", "Email": "quam.dignissim@convallisest.net", "City": "Delitzsch" },
{ "FirstName": "Jessamine", "LastName": "Bauer", "Email": "taciti.sociosqu@nibhvulputatemauris.co.uk", "City": "Offida" },
{ "FirstName": "Gillian", "LastName": "Livingston", "Email": "justo@atiaculisquis.com", "City": "Saskatoon" },
{ "FirstName": "Laith", "LastName": "Nicholson", "Email": "elit.pellentesque.a@diam.org", "City": "Tallahassee" },
{ "FirstName": "Paloma", "LastName": "Alston", "Email": "cursus@metus.org", "City": "Cache Creek" },
{ "FirstName": "Freya", "LastName": "Dunn", "Email": "Vestibulum.accumsan@metus.co.uk", "City": "Heist-aan-Zee" },
{ "FirstName": "Griffin", "LastName": "Rice", "Email": "justo@tortordictumeu.net", "City": "Montpelier" },
{ "FirstName": "Catherine", "LastName": "West", "Email": "malesuada.augue@elementum.com", "City": "Tarnów" },
{ "FirstName": "Jena", "LastName": "Chambers", "Email": "erat.Etiam.vestibulum@quamelementumat.net", "City": "Konya" },
{ "FirstName": "Neil", "LastName": "Rodriguez", "Email": "enim@facilisis.com", "City": "Kraków" },
{ "FirstName": "Freya", "LastName": "Charles", "Email": "metus@nec.net", "City": "Arzano" },
{ "FirstName": "Anastasia", "LastName": "Strong", "Email": "sit@vitae.edu", "City": "Polpenazze del Garda" },
{ "FirstName": "Bell", "LastName": "Simon", "Email": "mollis.nec.cursus@disparturientmontes.ca", "City": "Caxias do Sul" },
{ "FirstName": "Minerva", "LastName": "Allison", "Email": "Donec@nequeIn.edu", "City": "Rio de Janeiro" },
{ "FirstName": "Yoko", "LastName": "Dawson", "Email": "neque.sed@semper.net", "City": "Saint-Remy-Geest" },
{ "FirstName": "Nadine", "LastName": "Justice", "Email": "netus@et.edu", "City": "Calgary" },
{ "FirstName": "Hoyt", "LastName": "Rosa", "Email": "Nullam.ut.nisi@Aliquam.co.uk", "City": "Mold" },
{ "FirstName": "Shafira", "LastName": "Noel", "Email": "tincidunt.nunc@non.edu", "City": "Kitzbühel" },
{ "FirstName": "Jin", "LastName": "Nunez", "Email": "porttitor.tellus.non@venenatisamagna.net", "City": "Dreieich" },
{ "FirstName": "Barbara", "LastName": "Gay", "Email": "est.congue.a@elit.com", "City": "Overland Park" },
{ "FirstName": "Riley", "LastName": "Hammond", "Email": "tempor.diam@sodalesnisi.net", "City": "Smoky Lake" },
{ "FirstName": "Molly", "LastName": "Fulton", "Email": "semper@Naminterdumenim.net", "City": "Montese" },
{ "FirstName": "Dexter", "LastName": "Owen", "Email": "non.ante@odiosagittissemper.ca", "City": "Bousval" },
{ "FirstName": "Kuame", "LastName": "Merritt", "Email": "ornare.placerat.orci@nisinibh.ca", "City": "Solingen" },
{ "FirstName": "Maggie", "LastName": "Delgado", "Email": "Nam.ligula.elit@Cum.org", "City": "Tredegar" },
{ "FirstName": "Hanae", "LastName": "Washington", "Email": "nec.euismod@adipiscingelit.org", "City": "Amersfoort" },
{ "FirstName": "Jonah", "LastName": "Cherry", "Email": "ridiculus.mus.Proin@quispede.edu", "City": "Acciano" },
{ "FirstName": "Cheyenne", "LastName": "Munoz", "Email": "at@molestiesodalesMauris.edu", "City": "Saint-L?onard" },
{ "FirstName": "India", "LastName": "Mack", "Email": "sem.mollis@Inmi.co.uk", "City": "Maryborough" },
{ "FirstName": "Lael", "LastName": "Mcneil", "Email": "porttitor@risusDonecegestas.com", "City": "Livorno" },
{ "FirstName": "Jillian", "LastName": "Mckay", "Email": "vulputate.eu.odio@amagnaLorem.co.uk", "City": "Salvador" },
{ "FirstName": "Shaine", "LastName": "Wright", "Email": "malesuada@pharetraQuisqueac.org", "City": "Newton Abbot" },
{ "FirstName": "Keane", "LastName": "Richmond", "Email": "nostra.per.inceptos@euismodurna.org", "City": "Canterano" },
{ "FirstName": "Samuel", "LastName": "Davis", "Email": "felis@euenim.com", "City": "Peterhead" },
{ "FirstName": "Zelenia", "LastName": "Sheppard", "Email": "Quisque.nonummy@antelectusconvallis.org", "City": "Motta Visconti" },
{ "FirstName": "Giacomo", "LastName": "Cole", "Email": "aliquet.libero@urnaUttincidunt.ca", "City": "Donnas" },
{ "FirstName": "Mason", "LastName": "Hinton", "Email": "est@Nunc.co.uk", "City": "St. Asaph" },
{ "FirstName": "Katelyn", "LastName": "Koch", "Email": "velit.Aliquam@Suspendisse.edu", "City": "Cleveland" },
{ "FirstName": "Olga", "LastName": "Spencer", "Email": "faucibus@Praesenteudui.net", "City": "Karapınar" },
{ "FirstName": "Erasmus", "LastName": "Strong", "Email": "dignissim.lacus@euarcu.net", "City": "Passau" },
{ "FirstName": "Regan", "LastName": "Cline", "Email": "vitae.erat.vel@lacusEtiambibendum.co.uk", "City": "Pergola" },
{ "FirstName": "Stone", "LastName": "Holt", "Email": "eget.mollis.lectus@Aeneanegestas.ca", "City": "Houston" },
{ "FirstName": "Deanna", "LastName": "Branch", "Email": "turpis@estMauris.net", "City": "Olcenengo" },
{ "FirstName": "Rana", "LastName": "Green", "Email": "metus@conguea.edu", "City": "Onze-Lieve-Vrouw-Lombeek" },
{ "FirstName": "Caryn", "LastName": "Henson", "Email": "Donec.sollicitudin.adipiscing@sed.net", "City": "Kington" },
{ "FirstName": "Clarke", "LastName": "Stein", "Email": "nec@mollis.co.uk", "City": "Tenali" },
{ "FirstName": "Kelsie", "LastName": "Porter", "Email": "Cum@gravidaAliquam.com", "City": "İskenderun" },
{ "FirstName": "Cooper", "LastName": "Pugh", "Email": "Quisque.ornare.tortor@dictum.co.uk", "City": "Delhi" },
{ "FirstName": "Paul", "LastName": "Spencer", "Email": "ac@InfaucibusMorbi.com", "City": "Biez" },
{ "FirstName": "Cassady", "LastName": "Farrell", "Email": "Suspendisse.non@venenatisa.net", "City": "New Maryland" },
{ "FirstName": "Sydnee", "LastName": "Velazquez", "Email": "mollis@loremfringillaornare.com", "City": "Strée" },
{ "FirstName": "Felix", "LastName": "Boyle", "Email": "id.libero.Donec@aauctor.org", "City": "Edinburgh" },
{ "FirstName": "Ryder", "LastName": "House", "Email": "molestie@natoquepenatibus.org", "City": "Copertino" },
{ "FirstName": "Hadley", "LastName": "Holcomb", "Email": "penatibus@nisi.ca", "City": "Avadi" },
{ "FirstName": "Marsden", "LastName": "Nunez", "Email": "Nulla.eget.metus@facilisisvitaeorci.org", "City": "New Galloway" },
{ "FirstName": "Alana", "LastName": "Powell", "Email": "non.lobortis.quis@interdumfeugiatSed.net", "City": "Pitt Meadows" },
{ "FirstName": "Dennis", "LastName": "Wyatt", "Email": "Morbi.non@nibhQuisquenonummy.ca", "City": "Wrexham" },
{ "FirstName": "Karleigh", "LastName": "Walton", "Email": "nascetur.ridiculus@quamdignissimpharetra.com", "City": "Diksmuide" },
{ "FirstName": "Brielle", "LastName": "Donovan", "Email": "placerat@at.edu", "City": "Kolmont" },
{ "FirstName": "Donna", "LastName": "Dickerson", "Email": "lacus.pede.sagittis@lacusvestibulum.com", "City": "Vallepietra" },
{ "FirstName": "Eagan", "LastName": "Pate", "Email": "est.Nunc@cursusNunc.ca", "City": "Durness" },
{ "FirstName": "Carlos", "LastName": "Ramsey", "Email": "est.ac.facilisis@duinec.co.uk", "City": "Tiruvottiyur" },
{ "FirstName": "Regan", "LastName": "Murphy", "Email": "lectus.Cum@aptent.com", "City": "Candidoni" },
{ "FirstName": "Claudia", "LastName": "Spence", "Email": "Nunc.lectus.pede@aceleifend.co.uk", "City": "Augusta" },
{ "FirstName": "Genevieve", "LastName": "Parker", "Email": "ultrices@inaliquetlobortis.net", "City": "Forbach" },
{ "FirstName": "Marshall", "LastName": "Allison", "Email": "erat.semper.rutrum@odio.org", "City": "Landau" },
{ "FirstName": "Reuben", "LastName": "Davis", "Email": "Donec@auctorodio.edu", "City": "Schönebeck" },
{ "FirstName": "Ralph", "LastName": "Doyle", "Email": "pede.Suspendisse.dui@Curabitur.org", "City": "Linkebeek" },
{ "FirstName": "Constance", "LastName": "Gilliam", "Email": "mollis@Nulla.edu", "City": "Enterprise" },
{ "FirstName": "Serina", "LastName": "Jacobson", "Email": "dictum.augue@ipsum.net", "City": "Hérouville-Saint-Clair" },
{ "FirstName": "Charity", "LastName": "Byrd", "Email": "convallis.ante.lectus@scelerisquemollisPhasellus.co.uk", "City": "Brussegem" },
{ "FirstName": "Hyatt", "LastName": "Bird", "Email": "enim.Nunc.ut@nonmagnaNam.com", "City": "Gdynia" },
{ "FirstName": "Brent", "LastName": "Dunn", "Email": "ac.sem@nuncid.com", "City": "Hay-on-Wye" },
{ "FirstName": "Casey", "LastName": "Bonner", "Email": "id@ornareelitelit.edu", "City": "Kearny" },
{ "FirstName": "Hakeem", "LastName": "Gill", "Email": "dis@nonummyipsumnon.org", "City": "Portico e San Benedetto" },
{ "FirstName": "Stewart", "LastName": "Meadows", "Email": "Nunc.pulvinar.arcu@convallisdolorQuisque.net", "City": "Dignano" },
{ "FirstName": "Nomlanga", "LastName": "Wooten", "Email": "inceptos@turpisegestas.ca", "City": "Troon" },
{ "FirstName": "Sebastian", "LastName": "Watts", "Email": "Sed.diam.lorem@lorem.co.uk", "City": "Palermo" },
{ "FirstName": "Chelsea", "LastName": "Larsen", "Email": "ligula@Nam.net", "City": "Poole" },
{ "FirstName": "Cameron", "LastName": "Humphrey", "Email": "placerat@id.org", "City": "Manfredonia" },
{ "FirstName": "Juliet", "LastName": "Bush", "Email": "consectetuer.euismod@vitaeeratVivamus.co.uk", "City": "Lavacherie" },
{ "FirstName": "Caryn", "LastName": "Hooper", "Email": "eu.enim.Etiam@ridiculus.org", "City": "Amelia" }
];
return {
findContactsMatching : function(str) {
var result = [];
var s = str.toLowerCase();
for (var i = 0; i < data.length; i++) {
var c = data[i];
if(c['FirstName'].toLowerCase().indexOf(s) >= 0 || c['LastName'].toLowerCase().indexOf(s) >= 0 || c['Email'].toLowerCase().indexOf(s) >= 0) {
result.push(c)
}
}
return result;
}
}
}()
</script>
```
# examples/animations.md
```md
+++
title = "Animations"
template = "demo.html"
+++
htmx is designed to allow you to use [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions)
to add smooth animations and transitions to your web page using only CSS and HTML. Below are a few examples of
various animation techniques.
htmx also allows you to use the new [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
for creating animations.
### Basic CSS Animations {#basic}
### Color Throb
The simplest animation technique in htmx is to keep the `id` of an element stable across a content swap. If the
`id` of an element is kept stable, htmx will swap it in such a way that CSS transitions can be written between
the old version of the element and the new one.
Consider this div:
\`\`\`html
<style>
.smooth {
transition: all 1s ease-in;
}
</style>
<div id="color-demo" class="smooth" style="color:red"
hx-get="/colors" hx-swap="outerHTML" hx-trigger="every 1s">
Color Swap Demo
</div>
\`\`\`
This div will poll every second and will get replaced with new content which changes the `color` style to a new value
(e.g. `blue`):
\`\`\`html
<div id="color-demo" class="smooth" style="color:blue"
hx-get="/colors" hx-swap="outerHTML" hx-trigger="every 1s">
Color Swap Demo
</div>
\`\`\`
Because the div has a stable id, `color-demo`, htmx will structure the swap such that a CSS transition, defined on the
`.smooth` class, applies to the style update from `red` to `blue`, and smoothly transitions between them.
#### Demo {#throb-demo}
<style>
.smooth {
transition: all 1s ease-in;
}
</style>
<div id="color-demo" class="smooth" style="color:red"
hx-get="/colors" hx-swap="outerHTML" hx-trigger="every 1s">
Color Swap Demo
</div>
<script>
var colors = ['blue', 'green', 'orange', 'red'];
onGet("/colors", function () {
var color = colors.shift();
colors.push(color);
return '<div id="color-demo" hx-get="/colors" hx-swap="outerHTML" class="smooth" hx-trigger="every 1s" style="color:' + color + '">\n'+
' Color Swap Demo\n'+
'</div>\n'
});
</script>
### Smooth Progress Bar
The [Progress Bar](@/examples/progress-bar.md) demo uses this basic CSS animation technique as well, by updating the `length`
property of a progress bar element, allowing for a smooth animation.
## Swap Transitions {#swapping}
### Fade Out On Swap
If you want to fade out an element that is going to be removed when the request ends, you want to take advantage
of the `htmx-swapping` class with some CSS and extend the swap phase to be long enough for your animation to
complete. This can be done like so:
\`\`\`html
<style>
.fade-me-out.htmx-swapping {
opacity: 0;
transition: opacity 1s ease-out;
}
</style>
<button class="fade-me-out"
hx-delete="/fade_out_demo"
hx-swap="outerHTML swap:1s">
Fade Me Out
</button>
\`\`\`
#### Demo {#fade-swap-demo}
<style>
.fade-me-out.htmx-swapping {
opacity: 0;
transition: opacity 1s ease-out;
}
</style>
<button class="fade-me-out"
hx-delete="/fade_out_demo"
hx-swap="outerHTML swap:1s">
Delete Me
</button>
<script>
onDelete("/fade_out_demo", function () {return ""});
</script>
## Settling Transitions {#settling}
### Fade In On Addition
Building on the last example, we can fade in the new content by using the `htmx-added` class during the settle
phase. You can also write CSS transitions against the target, rather than the new content, by using the `htmx-settling`
class.
\`\`\`html
<style>
#fade-me-in.htmx-added {
opacity: 0;
}
#fade-me-in {
opacity: 1;
transition: opacity 1s ease-out;
}
</style>
<button id="fade-me-in"
class="btn primary"
hx-post="/fade_in_demo"
hx-swap="outerHTML settle:1s">
Fade Me In
</button>
\`\`\`
#### Demo {#fade-settle-demo}
<style>
#fade-me-in.htmx-added {
opacity: 0;
}
#fade-me-in {
opacity: 1;
transition: opacity 1s ease-out;
}
</style>
<button id="fade-me-in"
class="btn primary"
hx-post="/fade_me_in"
hx-swap="outerHTML settle:1s">
Fade Me In
</button>
<script>
onPost("/fade_me_in", function () {return "<button id=\"fade-me-in\"\n"+
" class=\"btn primary\"\n"+
" hx-post=\"/fade_me_in\"\n"+
" hx-swap=\"outerHTML settle:1s\">\n"+
" Fade Me In\n"+
"</button>"});
</script>
## Request In Flight Animation {#request}
You can also take advantage of the `htmx-request` class, which is applied to the element that triggers a request. Below
is a form that on submit will change its look to indicate that a request is being processed:
\`\`\`html
<style>
form.htmx-request {
opacity: .5;
transition: opacity 300ms linear;
}
</style>
<form hx-post="/name" hx-swap="outerHTML">
<label>Name:</label><input name="name"><br/>
<button class="btn primary">Submit</button>
</form>
\`\`\`
#### Demo {#request-demo}
<style>
form.htmx-request {
opacity: .5;
transition: opacity 300ms linear;
}
</style>
<div aria-live="polite">
<form hx-post="/name" hx-swap="outerHTML">
<label>Name:</label><input name="name"><br/>
<button class="btn primary">Submit</button>
</form>
</div>
<script>
onPost("/name", function(){ return "Submitted!"; });
</script>
## Using the htmx `class-tools` Extension
Many interesting animations can be created by using the [`class-tools`](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/class-tools/README.md) extension.
Here is an example that toggles the opacity of a div. Note that we set the toggle time to be a bit longer than
the transition time. This avoids flickering that can happen if the transition is interrupted by a class change.
\`\`\`html
<style>
.demo.faded {
opacity:.3;
}
.demo {
opacity:1;
transition: opacity ease-in 900ms;
}
</style>
<div class="demo" classes="toggle faded:1s">Toggle Demo</div>
\`\`\`
#### Demo {#class-tools-demo}
<style>
.demo.faded {
opacity:.3;
}
.demo {
opacity:1;
transition: opacity ease-in 900ms;
}
</style>
<div class="demo" classes="toggle faded:1s">Toggle Demo</div>
### Using the View Transition API {#view-transitions}
htmx provides access to the new [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
via the `transition` option of the [`hx-swap`](/attributes/hx-swap) attribute.
Below is an example of a swap that uses a view transition. The transition is tied to the outer div via a
`view-transition-name` property in CSS, and that transition is defined in terms of `::view-transition-old`
and `::view-transition-new`, using `@keyframes` to define the animation. (Fuller details on the View Transition
API can be found on the [Chrome Developer Page](https://developer.chrome.com/docs/web-platform/view-transitions/) on them.)
The old content of this transition should slide out to the left and the new content should slide in from the right.
Note that, as of this writing, the visual transition will only occur on Chrome 111+, but more browsers are expected to
implement this feature in the near future.
\`\`\`html
<style>
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(90px); }
}
@keyframes slide-to-left {
to { transform: translateX(-90px); }
}
.slide-it {
view-transition-name: slide-it;
}
::view-transition-old(slide-it) {
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(slide-it) {
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
</style>
<div class="slide-it">
<h1>Initial Content</h1>
<button class="btn primary" hx-get="/new-content" hx-swap="innerHTML transition:true" hx-target="closest div">
Swap It!
</button>
</div>
\`\`\`
#### Demo
<style>
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(90px); }
}
@keyframes slide-to-left {
to { transform: translateX(-90px); }
}
.slide-it {
view-transition-name: slide-it;
}
::view-transition-old(slide-it) {
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(slide-it) {
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
</style>
<div class="slide-it">
<h1>Initial Content</h1>
<button class="btn primary" hx-get="/new-content" hx-swap="innerHTML transition:true" hx-target="closest div">
Swap It!
</button>
</div>
<script>
var originalContent = htmx.find(".slide-it").innerHTML;
this.server.respondWith("GET", "/new-content", function(xhr){
xhr.respond(200, {}, "<h1>New Content</h1> <button class='btn danger' hx-get='/original-content' hx-swap='innerHTML transition:true' hx-target='closest div'>Restore It! </button>")
});
this.server.respondWith("GET", "/original-content", function(xhr){
xhr.respond(200, {}, originalContent)
});
</script>
## Conclusion
You can use the techniques above to create quite a few interesting and pleasing effects with plain old HTML while using htmx.
```
# examples/async-auth.md
```md
+++
title = "Async Authentication"
template = "demo.html"
+++
This example shows how to implement an an async auth token flow for htmx.
The technique we will use here will take advantage of the fact that you can delay requests
using the [`htmx:confirm`](@/events.md#htmx:confirm) event.
We first have a button that should not issue a request until an auth token has been retrieved:
\`\`\`html
<button hx-post="/example" hx-target="next output">
An htmx-Powered button
</button>
<output>
--
</output>
\`\`\`
Next we will add some scripting to work with an `auth` promise (returned by a library):
\`\`\`html
<script>
// auth is a promise returned by our authentication system
// await the auth token and store it somewhere
let authToken = null;
auth.then((token) => {
authToken = token
})
// gate htmx requests on the auth token
htmx.on("htmx:confirm", (e)=> {
// if there is no auth token
if(authToken == null) {
// stop the regular request from being issued
e.preventDefault()
// only issue it once the auth promise has resolved
auth.then(() => e.detail.issueRequest())
}
})
// add the auth token to the request as a header
htmx.on("htmx:configRequest", (e)=> {
e.detail.headers["AUTH"] = authToken
})
</script>
\`\`\`
Here we use a global variable, but you could use `localStorage` or whatever preferred mechanism
you want to communicate the authentication token to the `htmx:configRequest` event.
With this code in place, htmx will not issue requests until the `auth` promise has been resolved.
```
# examples/bulk-update.md
```md
+++
title = "Bulk Update"
template = "demo.html"
+++
This demo shows how to implement a common pattern where rows are selected and then bulk updated. This is
accomplished by putting a form around a table, with checkboxes in the table, and then including the checked
values in the form submission (`POST` request):
\`\`\`html
<form id="checked-contacts"
hx-post="/users"
hx-swap="innerHTML settle:3s"
hx-target="#toast">
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Active</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td>Joe Smith</td>
<td>joe@smith.org</td>
<td><input type="checkbox" name="active:joe@smith.org"></td>
</tr>
...
</tbody>
</table>
<input type="submit" value="Bulk Update" class="btn primary">
<output id="toast"></output>
</form>
\`\`\`
The server will bulk-update the statuses based on the values of the checkboxes.
We respond with a small toast message about the update to inform the user, and
use an `<output>` element to politely announce the update for accessibility. Note
that the `<output>` element is appropriate for announcing the result of an action
in a specific form, but if you need to announce general-purpose messages that are
not connected to a form it would make sense to use an ARIA live region, eg
`<p id="toast" aria-live="polite"></p>`.
\`\`\`css
#toast.htmx-settling {
opacity: 100;
}
#toast {
background: #E1F0DA;
opacity: 0;
transition: opacity 3s ease-out;
}
\`\`\`
The cool thing is that, because HTML form inputs already manage their own state,
we don't need to re-render any part of the users table. The active users are
already checked and the inactive ones unchecked!
You can see a working example of this code below.
<style scoped="">
#toast.htmx-settling {
opacity: 100;
}
#toast {
background: #E1F0DA;
opacity: 0;
transition: opacity 3s ease-out;
}
</style>
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
const dataStore = (() => {
const data = {
"joe@smith.org": {name: 'Joe Smith', status: 'Active'},
"angie@macdowell.org": {name: 'Angie MacDowell', status: 'Active'},
"fuqua@tarkenton.org": {name: 'Fuqua Tarkenton', status: 'Active'},
"kim@yee.org": {name: 'Kim Yee', status: 'Inactive'},
};
return {
all() {
return data;
},
activate(email) {
if (data[email].status === 'Active') {
return 0;
} else {
data[email].status = 'Active';
return 1;
}
},
deactivate(email) {
if (data[email].status === 'Inactive') {
return 0;
} else {
data[email].status = 'Inactive';
return 1;
}
},
};
})();
// routes
init("/demo", function(request){
return displayUI(dataStore.all());
});
/*
Params look like:
{"active:joe@smith.org":"on","active:angie@macdowell.org":"on","active:fuqua@tarkenton.org":"on"}
*/
onPost("/users", function (req, params) {
const actives = {};
let activated = 0;
let deactivated = 0;
// Build a set of active users for efficient lookup
for (const param of Object.keys(params)) {
const nameEmail = param.split(':');
if (nameEmail[0] === 'active') {
actives[nameEmail[1]] = true;
}
}
// Activate or deactivate users based on the lookup
for (const email of Object.keys(dataStore.all())) {
if (actives[email]) {
activated += dataStore.activate(email);
} else {
deactivated += dataStore.deactivate(email);
}
}
return `Activated ${activated} and deactivated ${deactivated} users`;
});
// templates
function displayUI(contacts) {
return `<h3>Select Rows And Activate Or Deactivate Below</h3>
<form
id="checked-contacts"
hx-post="/users"
hx-swap="innerHTML settle:3s"
hx-target="#toast"
>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Active</th>
</tr>
</thead>
<tbody id="tbody">
${displayTable(contacts)}
</tbody>
</table>
<input type="submit" value="Bulk Update" class="btn primary">
<output id="toast"></output>
</form>
<br>`;
}
function displayTable(contacts) {
var txt = "";
for (email of Object.keys(contacts)) {
txt += `
<tr>
<td>${contacts[email].name}</td>
<td>${email}</td>
<td>
<input
type="checkbox"
name="active:${email}"
${contacts[email].status === 'Active' ? 'checked' : ''}>
</td>
</tr>
`;
}
return txt;
}
</script>
```
# examples/click-to-edit.md
```md
+++
title = "Click to Edit"
template = "demo.html"
+++
The click to edit pattern provides a way to offer inline editing of all or part of a record without a page refresh.
* This pattern starts with a UI that shows the details of a contact. The div has a button that will get the editing UI for the contact from `/contact/1/edit`
\`\`\`html
<div hx-target="this" hx-swap="outerHTML">
<div><label>First Name</label>: Joe</div>
<div><label>Last Name</label>: Blow</div>
<div><label>Email</label>: joe@blow.com</div>
<button hx-get="/contact/1/edit" class="btn primary">
Click To Edit
</button>
</div>
\`\`\`
* This returns a form that can be used to edit the contact
\`\`\`html
<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">
<div>
<label>First Name</label>
<input type="text" name="firstName" value="Joe">
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value="Blow">
</div>
<div class="form-group">
<label>Email Address</label>
<input type="email" name="email" value="joe@blow.com">
</div>
<button class="btn">Submit</button>
<button class="btn" hx-get="/contact/1">Cancel</button>
</form>
\`\`\`
* The form issues a `PUT` back to `/contact/1`, following the usual REST-ful pattern.
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// data
var contact = {
"firstName" : "Joe",
"lastName" : "Blow",
"email" : "joe@blow.com"
};
// routes
init("/contact/1", function(request){
return displayTemplate(contact);
});
onGet("/contact/1/edit", function(request){
return formTemplate(contact);
});
onPut("/contact/1", function (req, params) {
contact.firstName = params['firstName'];
contact.lastName = params['lastName'];
contact.email = params['email'];
return displayTemplate(contact);
});
// templates
function formTemplate(contact) {
return `<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">
<div>
<label for="firstName">First Name</label>
<input autofocus type="text" id="firstName" name="firstName" value="${contact.firstName}">
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" name="lastName" value="${contact.lastName}">
</div>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" value="${contact.email}">
</div>
<button class="btn primary" type="submit">Submit</button>
<button class="btn danger" hx-get="/contact/1">Cancel</button>
</form>`
}
function displayTemplate(contact) {
return `<div hx-target="this" hx-swap="outerHTML">
<div><label>First Name</label>: ${contact.firstName}</div>
<div><label>Last Name</label>: ${contact.lastName}</div>
<div><label>Email Address</label>: ${contact.email}</div>
<button hx-get="/contact/1/edit" class="btn primary">
Click To Edit
</button>
</div>`;
}
</script>
```
# examples/click-to-load.md
```md
+++
title = "Click to Load"
template = "demo.html"
+++
This example shows how to implement click-to-load the next page in a table of data. The crux of the demo is
the final row:
\`\`\`html
<tr id="replaceMe">
<td colspan="3">
<button class='btn primary' hx-get="/contacts/?page=2"
hx-target="#replaceMe"
hx-swap="outerHTML">
Load More Agents... <img class="htmx-indicator" src="/img/bars.svg">
</button>
</td>
</tr>
\`\`\`
This row contains a button that will replace the entire row with the next page of
results (which will contain a button to load the *next* page of results). And so on.
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// data
var dataStore = function(){
var contactId = 9;
function generateContact() {
contactId++;
var idHash = "";
var possible = "ABCDEFG0123456789";
for( var i=0; i < 15; i++ ) idHash += possible.charAt(Math.floor(Math.random() * possible.length));
return { name: "Agent Smith", email: "void" + contactId + "@null.org", id: idHash }
}
return {
contactsForPage : function(page) {
var vals = [];
for( var i=0; i < 10; i++ ){
vals.push(generateContact());
}
return vals;
}
}
}()
// routes
init("/demo", function(request, params){
var contacts = dataStore.contactsForPage(1)
return tableTemplate(contacts)
});
onGet(/\/contacts.*/, function(request, params){
var page = parseInt(params['page']);
var contacts = dataStore.contactsForPage(page)
return rowsTemplate(page, contacts);
});
// templates
function tableTemplate(contacts) {
return `<table><thead><tr><th>Name</th><th>Email</th><th>ID</th></tr></thead><tbody>
${rowsTemplate(1, contacts)}
</tbody></table>`
}
function rowsTemplate(page, contacts) {
var txt = "";
for (var i = 0; i < contacts.length; i++) {
var c = contacts[i];
txt += `<tr><td>${c.name}</td><td>${c.email}</td><td>${c.id}</td></tr>\n`;
}
txt += loadMoreRow(page);
return txt;
}
function loadMoreRow(page) {
return `<tr id="replaceMe">
<td colspan="3">
<center>
<button class='btn primary' hx-get="/contacts/?page=${page + 1}"
hx-target="#replaceMe"
hx-swap="outerHTML">
Load More Agents... <img class="htmx-indicator" src="/img/bars.svg">
</button>
</center>
</td>
</tr>`;
}
</script>
```
# examples/confirm.md
```md
+++
title = "A Customized Confirmation UI"
template = "demo.html"
+++
htmx supports the [`hx-confirm`](@/attributes/hx-confirm.md) attribute to provide a simple mechanism for confirming a user
action. This uses the default `confirm()` function in javascript which, while trusty, may not be consistent with your
applications UX.
In this example we will see how to use [sweetalert2](https://sweetalert2.github.io) to implement a custom confirmation dialog. Below are two
examples, one using a click+custom event method, and one using the built-in `hx-confirm` attribute and
the [`htmx:confirm`](@/events.md#htmx:confirm) event.
## Using on click+custom event
\`\`\`html
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<button hx-get="/confirmed"
hx-trigger='confirmed'
onClick="Swal.fire({title: 'Confirm', text:'Do you want to continue?'}).then((result)=>{
if(result.isConfirmed){
htmx.trigger(this, 'confirmed');
}
})">
Click Me
</button>
\`\`\`
Here we use javascript to show a Sweet Alert 2 on a click, asking for confirmation. If the user confirms
the dialog, we then trigger the request by triggering the custom "confirmed" event
which is then picked up by `hx-trigger`.
## Vanilla JS, hx-confirm
\`\`\`html
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener("htmx:confirm", function(e) {
// The event is triggered on every trigger for a request, so we need to check if the element
// that triggered the request has a hx-confirm attribute, if not we can return early and let
// the default behavior happen
if (!e.detail.elt.hasAttribute('hx-confirm')) return
// This will prevent the request from being issued to later manually issue it
e.preventDefault()
Swal.fire({
title: "Proceed?",
text: `I ask you... ${e.detail.question}`
}).then(function(result) {
if (result.isConfirmed) {
// If the user confirms, we manually issue the request
e.detail.issueRequest(true); // true to skip the built-in window.confirm()
}
})
})
</script>
<button hx-get="/confirmed" hx-confirm="Some confirm text here">
Click Me
</button>
\`\`\`
We add some javascript to invoke Sweet Alert 2 on a click, asking for confirmation. If the user confirms
the dialog, we trigger the request by calling the `issueRequest` method. We pass `skipConfirmation=true` as argument to skip `window.confirm`.
This allows to use `hx-confirm`'s value in the prompt which is convenient
when the question depends on the element e.g. a django list:
\`\`\`html
{% for client in clients %}
<button hx-post="/delete/{{client.pk}}" hx-confirm="Delete {{client.name}}??">Delete</button>
{% endfor %}
\`\`\`
Learn more about the [`htmx:confirm`](@/events.md#htmx:confirm) event [here](@/events.md#htmx:confirm).
```
# examples/delete-row.md
```md
+++
title = "Delete Row"
template = "demo.html"
+++
This example shows how to implement a delete button that removes a table row upon completion. First let's look at the
table body:
\`\`\`html
<table class="table delete-row-example">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody hx-confirm="Are you sure?" hx-target="closest tr" hx-swap="outerHTML swap:1s">
...
</tbody>
</table>
\`\`\`
The table body has a [`hx-confirm`](@/attributes/hx-confirm.md) attribute to confirm the delete action. It also
set the target to be the `closest tr` that is, the closest table row, for all the buttons ([`hx-target`](@/attributes/hx-target.md)
is inherited from parents in the DOM.) The swap specification in [`hx-swap`](@/attributes/hx-swap.md) says to swap the
entire target out and to wait 1 second after receiving a response. This last bit is so that we can use the following
CSS:
\`\`\`css
tr.htmx-swapping td {
opacity: 0;
transition: opacity 1s ease-out;
}
\`\`\`
To fade the row out before it is swapped/removed.
Each row has a button with a [`hx-delete`](@/attributes/hx-delete.md) attribute containing the url on which to issue a `DELETE`
request to delete the row from the server. This request responds with a `200` status code and empty content, indicating that the
row should be replaced with nothing.
\`\`\`html
<tr>
<td>Angie MacDowell</td>
<td>angie@macdowell.org</td>
<td>Active</td>
<td>
<button class="btn danger" hx-delete="/contact/1">
Delete
</button>
</td>
</tr>
\`\`\`
<style>
tr.htmx-swapping td {
opacity: 0;
transition: opacity 1s ease-out;
}
</style>
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// data
var contacts = [
{
name: "Joe Smith",
email: "joe@smith.org",
status: "Active",
},
{
name: "Angie MacDowell",
email: "angie@macdowell.org",
status: "Active",
},
{
name: "Fuqua Tarkenton",
email: "fuqua@tarkenton.org",
status: "Active",
},
{
name: "Kim Yee",
email: "kim@yee.org",
status: "Inactive",
},
];
// routes
init("/demo", function(request, params){
return tableTemplate(contacts);
});
onDelete(/\/contact\/\d+/, function(request, params){
return "";
});
// templates
function rowTemplate(contact, i) {
return `<tr>
<td>${contact["name"]}</td>
<td>${contact["email"]}</td>
<td>${contact["status"]}</td>
<td>
<button class="btn danger" hx-delete="/contact/${i}">
Delete
</button>
</td>
</tr>`;
}
function tableTemplate(contacts) {
var rows = "";
for (var i = 0; i < contacts.length; i++) {
rows += rowTemplate(contacts[i], i, "");
}
return `
<table class="table delete-row-example">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody hx-confirm="Are you sure?" hx-target="closest tr" hx-swap="outerHTML swap:1s">
${rows}
</tbody>
</table>`;
}
</script>
```
# examples/dialogs.md
```md
+++
title = "Dialogs"
template = "demo.html"
+++
Dialogs can be triggered with the [`hx-prompt`](@/attributes/hx-prompt.md) and [`hx-confirm`](@/attributes/hx-confirm.md)attributes. These are triggered by the user interaction that would trigger the AJAX request, but the request is only sent if the dialog is accepted.
\`\`\`html
<div>
<button class="btn primary"
hx-post="/submit"
hx-prompt="Enter a string"
hx-confirm="Are you sure?"
hx-target="#response">
Prompt Submission
</button>
<div id="response"></div>
</div>
\`\`\`
The value provided by the user to the prompt dialog is sent to the server in a `HX-Prompt` header. In this case, the server simply echos the user input back.
\`\`\`html
User entered <i>${response}</i>
\`\`\`
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params){
return submitButton();
});
onPost("/submit", function(request, params){
var response = request.requestHeaders['HX-Prompt'];
return promptSubmit(response);
});
// templates
function submitButton() {
return `<div>
<button class="btn primary"
hx-post="/submit"
hx-prompt="Enter a string"
hx-confirm="Are you sure?"
hx-target="#response">
Prompt Submission
</button>
<div id="response"></div>
</div>`;
}
function promptSubmit(response) {
return `User entered <i>${response}</i>`;
}
</script>
```
# examples/edit-row.md
```md
+++
title = "Edit Row"
template = "demo.html"
+++
This example shows how to implement editable rows. First let's look at the table body:
\`\`\`html
<table class="table delete-row-example">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody hx-target="closest tr" hx-swap="outerHTML">
...
</tbody>
</table>
\`\`\`
This will tell the requests from within the table to target the closest enclosing row that the request is triggered
on and to replace the entire row.
Here is the HTML for a row:
\`\`\`html
<tr>
<td>${contact.name}</td>
<td>${contact.email}</td>
<td>
<button class="btn danger"
hx-get="/contact/${contact.id}/edit"
hx-trigger="edit"
onClick="let editing = document.querySelector('.editing')
if(editing) {
Swal.fire({title: 'Already Editing',
showCancelButton: true,
confirmButtonText: 'Yep, Edit This Row!',
text:'Hey! You are already editing a row! Do you want to cancel that edit and continue?'})
.then((result) => {
if(result.isConfirmed) {
htmx.trigger(editing, 'cancel')
htmx.trigger(this, 'edit')
}
})
} else {
htmx.trigger(this, 'edit')
}">
Edit
</button>
</td>
</tr>
\`\`\`
Here we are getting a bit fancy and only allowing one row at a time to be edited, using some JavaScript.
We check to see if there is a row with the `.editing` class on it and confirm that the user wants to edit this row
and dismiss the other one. If so, we send a cancel event to the other row so it will issue a request to go back to
its initial state.
We then trigger the `edit` event on the current element, which triggers the htmx request to get the editable version
of the row.
Note that if you didn't care if a user was editing multiple rows, you could omit the hyperscript and custom `hx-trigger`,
and just let the normal click handling work with htmx. You could also implement mutual exclusivity by simply targeting the
entire table when the Edit button was clicked. Here we wanted to show how to integrate htmx and JavaScript to solve
the problem and narrow down the server interactions a bit, plus we get to use a nice SweetAlert confirm dialog.
Finally, here is what the row looks like when the data is being edited:
\`\`\`html
<tr hx-trigger='cancel' class='editing' hx-get="/contact/${contact.id}">
<td><input autofocus name='name' value='${contact.name}'></td>
<td><input name='email' value='${contact.email}'></td>
<td>
<button class="btn danger" hx-get="/contact/${contact.id}">
Cancel
</button>
<button class="btn danger" hx-put="/contact/${contact.id}" hx-include="closest tr">
Save
</button>
</td>
</tr>
\`\`\`
Here we have a few things going on: First off the row itself can respond to the `cancel` event, which will bring
back the read-only version of the row. There is a cancel button that allows
cancelling the current edit. Finally, there is a save button that issues a `PUT` to update the contact. Note that
there is an [`hx-include`](@/attributes/hx-include.md) that includes all the inputs in the closest row. Tables rows are
notoriously difficult to use with forms due to HTML constraints (you can't put a `form` directly inside a `tr`) so
this makes things a bit nicer to deal with.
{{ demoenv() }}
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// data
var contacts = [
{
name: "Joe Smith",
email: "joe@smith.org",
status: "Active",
id: 0
},
{
name: "Angie MacDowell",
email: "angie@macdowell.org",
status: "Active",
id: 1
},
{
name: "Fuqua Tarkenton",
email: "fuqua@tarkenton.org",
status: "Active",
id: 2
},
{
name: "Kim Yee",
email: "kim@yee.org",
status: "Inactive",
id: 3
},
];
// routes
init("/demo", function(request, params){
return tableTemplate(contacts);
});
onGet(/\/contact\/\d+/, function(request, params){
var id = parseInt(request.url.split("/")[2]); // get the contact
var contact = contacts[id];
console.log(request, id, contact)
if(request.url.endsWith("/edit")) {
return editTemplate(contacts[id])
} else {
return rowTemplate(contacts[id])
}
});
onPut(/\/contact\/\d+/, function(request, params){
var id = parseInt(request.url.split("/")[2]); // get the contact
contact = contacts[id]
contact.name = params['name'];
contact.email = params['email'];
return rowTemplate(contact);
});
// templates
function rowTemplate(contact) {
return `<tr>
<td>${contact.name}</td>
<td>${contact.email}</td>
<td>
<button class="btn danger"
hx-get="/contact/${contact.id}/edit"
hx-trigger="edit"
onClick="let editing = document.querySelector('.editing')
if(editing) {
Swal.fire({title: 'Already Editing',
showCancelButton: true,
confirmButtonText: 'Yep, Edit This Row!',
text:'Hey! You are already editing a row! Do you want to cancel that edit and continue?'})
.then((result) => {
if(result.isConfirmed) {
htmx.trigger(editing, 'cancel')
htmx.trigger(this, 'edit')
}
})
} else {
htmx.trigger(this, 'edit')
}">
Edit
</button>
</td>
</tr>`;
}
function editTemplate(contact) {
return `<tr hx-trigger='cancel' class='editing' hx-get="/contact/${contact.id}">
<td><input autofocus name='name' value='${contact.name}'</td>
<td><input name='email' value='${contact.email}'</td>
<td>
<button class="btn danger" hx-get="/contact/${contact.id}">
Cancel
</button>
<button class="btn danger" hx-put="/contact/${contact.id}" hx-include="closest tr">
Save
</button>
</td>
</tr>`;
}
function tableTemplate(contacts) {
var rows = "";
for (var i = 0; i < contacts.length; i++) {
rows += rowTemplate(contacts[i], i, "");
}
return `
<table class="table delete-row-example">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody hx-target="closest tr" hx-swap="outerHTML">
${rows}
</tbody>
</table>`;
}
</script>
```
# examples/file-upload-input.md
```md
+++
title = "Preserving File Inputs after Form Errors"
template = "demo.html"
+++
When using server-side error handling and validation with forms that include both primitive values and file inputs, the file input's value is lost when the form returns with error messages. Consequently, users are required to re-upload the file, resulting in a less user-friendly experience.
To overcome the problem of losing the file input value, you can use the `hx-preserve` attribute on the `input` element:
\`\`\`html
<form method="POST" id="binaryForm" enctype="multipart/form-data" hx-swap="outerHTML" hx-target="#binaryForm">
<input hx-preserve id="someId" type="file" name="binaryFile">
<!-- Other code here, such as input error handling. -->
<button type="submit">Submit</button>
</form>
\`\`\`
If the file field is returned with errors on it, they will be displayed provided that `hx-preserve` was placed in the `input` only and not the element that would show the errors (e.g. `ol.errorlist`). If in a given circumstance you want the file upload input to return *without* preserving the user's chosen file (for example, because the file was an invalid type), you can manage that on the server side by omitting the `hx-preserve` attribute when the field has the relevant errors.
Alternatively, you can preserve file inputs after form errors by restructuring the form so that the file input is located outside the area that will be swapped.
Before:
\`\`\`html
<form method="POST" id="binaryForm" enctype="multipart/form-data" hx-swap="outerHTML" hx-target="#binaryForm">
<input type="file" name="binaryFile">
<button type="submit">Submit</button>
</form>
\`\`\`
After:
\`\`\`html
<input form="binaryForm" type="file" name="binaryFile">
<form method="POST" id="binaryForm" enctype="multipart/form-data" hx-swap="outerHTML" hx-target="#binaryForm">
<button type="submit">Submit</button>
</form>
\`\`\`
1. Form Restructuring: Move the binary file input outside the main form element in the HTML structure.
2. Using the form Attribute: Enhance the binary file input by adding the form attribute and setting its value to the ID of the main form. This linkage associates the binary file input with the form, even when it resides outside the form element.
```
# examples/file-upload.md
```md
+++
title = "File Upload"
template = "demo.html"
+++
In this example we show how to create a file upload form that will be submitted via ajax, along
with a progress bar.
We will show two different implementation, one in pure javascript (using some utility methods in htmx) and one in [hyperscript](https://hyperscript.org)
First the pure javascript version.
* We have a form of type `multipart/form-data` so that the file will be properly encoded
* We post the form to `/upload`
* We have a `progress` element
* We listen for the `htmx:xhr:progress` event and update the `value` attribute of the progress bar based on the `loaded` and `total` properties in the event detail.
\`\`\`html
<form id='form' hx-encoding='multipart/form-data' hx-post='/upload'>
<input type='file' name='file'>
<button>
Upload
</button>
<progress id='progress' value='0' max='100'></progress>
</form>
<script>
htmx.on('#form', 'htmx:xhr:progress', function(evt) {
htmx.find('#progress').setAttribute('value', evt.detail.loaded/evt.detail.total * 100)
});
</script>
\`\`\`
The Hyperscript version is very similar, except:
* The script is embedded directly on the form element
* Hyperscript offers nicer syntax (although the htmx API is pretty nice too!)
\`\`\`html
<form hx-encoding='multipart/form-data' hx-post='/upload'
_='on htmx:xhr:progress(loaded, total) set #progress.value to (loaded/total)*100'>
<input type='file' name='file'>
<button>
Upload
</button>
<progress id='progress' value='0' max='100'></progress>
</form>
\`\`\`
Note that hyperscript allows you to destructure properties from `details` directly into variables
```
# examples/infinite-scroll.md
```md
+++
title = "Infinite Scroll"
template = "demo.html"
+++
The infinite scroll pattern provides a way to load content dynamically on user scrolling action.
Let's focus on the final row (or the last element of your content):
\`\`\`html
<tr hx-get="/contacts/?page=2"
hx-trigger="revealed"
hx-swap="afterend">
<td>Agent Smith</td>
<td>void29@null.org</td>
<td>55F49448C0</td>
</tr>
\`\`\`
This last element contains a listener which, when scrolled into view, will trigger a request. The result is then appended after it.
The last element of the results will itself contain the listener to load the *next* page of results, and so on.
> `revealed` - triggered when an element is scrolled into the viewport (also useful for lazy-loading). If you are using `overflow` in css like `overflow-y: scroll` you should use `intersect once` instead of `revealed`.
{{ demoenv() }}
<script>
server.autoRespondAfter = 1000; // longer response for more drama
//=========================================================================
// Fake Server Side Code
//=========================================================================
// data
var dataStore = function(){
var contactId = 9;
function generateContact() {
contactId++;
var idHash = "";
var possible = "ABCDEFG0123456789";
for( var i=0; i < 10; i++ ) idHash += possible.charAt(Math.floor(Math.random() * possible.length));
return { name: "Agent Smith", email: "void" + contactId + "@null.org", id: idHash }
}
return {
contactsForPage : function(page) {
var vals = [];
for( var i=0; i < 20; i++ ){
vals.push(generateContact());
}
return vals;
}
}
}()
// routes
init("/demo", function(request, params){
var contacts = dataStore.contactsForPage(1)
return tableTemplate(contacts)
});
onGet(/\/contacts.*/, function(request, params){
var page = parseInt(params['page']);
var contacts = dataStore.contactsForPage(page)
return rowsTemplate(page, contacts);
});
// templates
function tableTemplate(contacts) {
return `<table hx-indicator=".htmx-indicator"><thead><tr><th>Name</th><th>Email</th><th>ID</th></tr></thead><tbody>
${rowsTemplate(1, contacts)}
</tbody></table><center><img class="htmx-indicator" width="60" src="/img/bars.svg"></center>`
}
function rowsTemplate(page, contacts) {
var txt = "";
var trigger_attributes = "";
for (var i = 0; i < contacts.length; i++) {
var c = contacts[i];
if (i == (contacts.length - 1)) {
trigger_attributes = ` hx-get="/contacts/?page=${page + 1}" hx-trigger="revealed" hx-swap="afterend"`
}
txt += "<tr" + trigger_attributes +"><td>" + c.name + "</td><td>" + c.email + "</td><td>" + c.id + "</td></tr>\n";
}
return txt;
}
</script>
```
# examples/inline-validation.md
```md
+++
title = "Inline Validation"
template = "demo.html"
+++
This example shows how to do inline field validation, in this case of an email address. To do this
we need to create a form with an input that `POST`s back to the server with the value to be validated
and updates the DOM with the validation results.
We start with this form:
\`\`\`html
<h3>Signup Form</h3>
<form hx-post="/contact">
<div hx-target="this" hx-swap="outerHTML">
<label>Email Address</label>
<input name="email" hx-post="/contact/email" hx-indicator="#ind">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
</div>
<div class="form-group">
<label>First Name</label>
<input type="text" class="form-control" name="firstName">
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" class="form-control" name="lastName">
</div>
<button class="btn primary">Submit</button>
</form>
\`\`\`
Note that the first div in the form has set itself as the target of the request and specified the `outerHTML`
swap strategy, so it will be replaced entirely by the response. The input then specifies that it will
`POST` to `/contact/email` for validation, when the `changed` event occurs (this is the default for inputs).
It also specifies an indicator for the request.
When a request occurs, it will return a partial to replace the outer div. It might look like this:
\`\`\`html
<div hx-target="this" hx-swap="outerHTML" class="error">
<label>Email Address</label>
<input name="email" hx-post="/contact/email" hx-indicator="#ind" value="test@foo.com">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
<div class='error-message'>That email is already taken. Please enter another email.</div>
</div>
\`\`\`
Note that this div is annotated with the `error` class and includes an error message element.
This form can be lightly styled with this CSS:
\`\`\`css
.error-message {
color:red;
}
.error input {
box-shadow: 0 0 3px #CC0000;
}
.valid input {
box-shadow: 0 0 3px #36cc00;
}
\`\`\`
To give better visual feedback.
Below is a working demo of this example. The only email that will be accepted is `test@test.com`.
<style>
.error-message {
color:red;
}
.error input {
box-shadow: 0 0 3px #CC0000;
}
.valid input {
box-shadow: 0 0 3px #36cc00;
}
</style>
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params){
return demoTemplate();
});
onPost("/contact", function(request, params){
return formTemplate();
});
onPost(/\/contact\/email.*/, function(request, params){
var email = params['email'];
if(!/\S+@\S+\.\S+/.test(email)) {
return emailInputTemplate(email, "Please enter a valid email address");
} else if(email != "test@test.com") {
return emailInputTemplate(email, "That email is already taken. Please enter another email.");
} else {
return emailInputTemplate(email);
}
});
// templates
function demoTemplate() {
return `<h3>Signup Form</h3><p>Enter an email into the input below and on tab out it will be validated. Only "test@test.com" will pass.</p> ` + formTemplate();
}
function formTemplate() {
return `<form hx-post="/contact">
<div hx-target="this" hx-swap="outerHTML">
<label for="email">Email Address</label>
<input name="email" id="email" hx-post="/contact/email" hx-indicator="#ind">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
</div>
<div class="form-group">
<label for="firstName">First Name</label>
<input type="text" class="form-control" name="firstName" id="firstName">
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input type="text" class="form-control" name="lastName" id="lastName">
</div>
<button type='submit' class="btn primary" disabled>Submit</button>
</form>`;
}
function emailInputTemplate(val, errorMsg) {
return `<div hx-target="this" hx-swap="outerHTML" class="${errorMsg ? "error" : "valid"}">
<label>Email Address</label>
<input name="email" hx-post="/contact/email" hx-indicator="#ind" value="${val}" aria-invalid="${!!errorMsg}">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
${errorMsg ? (`<div class='error-message' >${errorMsg}</div>`) : ""}
</div>`;
}
</script>
```
# examples/keyboard-shortcuts.md
```md
+++
title = "Keyboard Shortcuts"
template = "demo.html"
+++
In this example we show how to create a keyboard shortcut for an action.
We start with a simple button that loads some content from the server:
\`\`\`html
<button class="btn primary" hx-trigger="click, keyup[altKey&&shiftKey&&key=='D'] from:body"
hx-post="/doit">Do It! (alt-shift-D)</button>
\`\`\`
Note that the button responds to both the `click` event (as usual) and also the keyup event when `alt-shift-D` is pressed.
The `from:` modifier is used to listen for the keyup event on the `body` element, thus making it a "global" keyboard
shortcut.
You can trigger the demo below by either clicking on the button, or by hitting alt-shift-D.
You can find out the conditions needed for a given keyboard shortcut here:
[https://javascript.info/keyboard-events](https://javascript.info/keyboard-events)
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/init", function(request, params){
return "<button class='btn primary' style='font-size:20pt' hx-trigger='click, keyup[altKey&&shiftKey&&key==\"D\"] from:body'" +
" hx-post='/doit'>Do It! (alt-shift-D) </button>";
});
onPost("/doit", function (request, params) {
return "Did it!";
});
</script>
```
# examples/lazy-load.md
```md
+++
title = "Lazy Loading"
template = "demo.html"
+++
This example shows how to lazily load an element on a page. We start with an initial
state that looks like this:
\`\`\`html
<div hx-get="/graph" hx-trigger="load">
<img alt="Result loading..." class="htmx-indicator" width="150" src="/img/bars.svg"/>
</div>
\`\`\`
Which shows a progress indicator as we are loading the graph. The graph is then
loaded and faded gently into view via a settling CSS transition:
\`\`\`css
.htmx-settling img {
opacity: 0;
}
img {
transition: opacity 300ms ease-in;
}
\`\`\`
<style>
.htmx-settling img {
opacity: 0;
}
img {
transition: opacity 300ms ease-in;
}
</style>
{{ demoenv() }}
<script>
server.autoRespondAfter = 2000; // longer response for more drama
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params){
return lazyTemplate();
});
onGet("/graph", function(request, params){
return "<img alt='Tokyo Climate' src='/img/tokyo.png'>";
});
// templates
function lazyTemplate(page) {
return `<div hx-get="/graph" hx-trigger="load">
<img alt="Result loading..." class="htmx-indicator" width="150" src="/img/bars.svg"/>
</div>`;
}
</script>
```
# examples/modal-bootstrap.md
```md
+++
title = "Modal Dialogs in Bootstrap"
template = "demo.html"
+++
Many CSS toolkits include styles (and Javascript) for creating modal dialog boxes.
This example shows how to use HTMX alongside original JavaScript provided by Bootstrap.
We start with a button that triggers the dialog, along with a DIV at the bottom of your
markup where the dialog will be loaded:
\`\`\`html
<button
hx-get="/modal"
hx-target="#modals-here"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#modals-here"
class="btn primary">Open Modal</button>
<div id="modals-here"
class="modal modal-blur fade"
style="display: none"
aria-hidden="false"
tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content"></div>
</div>
</div>
\`\`\`
This button uses a `GET` request to `/modal` when this button is clicked. The
contents of this file will be added to the DOM underneath the `#modals-here` DIV.
The server responds with a slightly modified version of Bootstrap's standard modal
\`\`\`html
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
\`\`\`
<div id="modals-here"
class="modal modal-blur fade"
style="display: none"
aria-hidden="false"
tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content"></div>
</div>
</div>
{{ demoenv() }}
<style>
@import "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.2.2/css/bootstrap.min.css";
</style>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params) {
return `<button
hx-get="/modal"
hx-target="#modals-here"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#modals-here"
class="btn primary">Open Modal</button>
`})
onGet("/modal", function(request, params){
return `<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>`
});
</script>
```
# examples/modal-custom.md
```md
+++
title = "Custom Modal Dialogs"
template = "demo.html"
+++
While htmx works great with dialogs built into CSS frameworks (like [Bootstrap](@/examples/modal-bootstrap.md) and [UIKit](@/examples/modal-uikit.md)), htmx also makes
it easy to build modal dialogs from scratch. Here is a quick example of one way to build them.
Click here to see a demo of the final result:
<button class="btn primary" hx-get="/modal" hx-target="body" hx-swap="beforeend">Open a Modal</button>
## High Level Plan
We're going to make a button that loads remote content from the server, then displays it in a modal dialog. The modal
content will be added to the end of the `<body>` element, in a div named `#modal`.
In this demo we'll define some nice animations in CSS, and then use some [Hyperscript](https://hyperscript.org) to remove the
modals from the DOM when the user is done. Hyperscript is *not* required with htmx, but the two were designed to be used
together and it is much nicer for writing async & event oriented code than JavaScript, which is why we chose it for this
example.
## Main Page HTML
\`\`\`html
<button class="btn primary" hx-get="/modal" hx-target="body" hx-swap="beforeend">Open a Modal</button>
\`\`\`
## Modal HTML Fragment
\`\`\`html
<div id="modal" _="on closeModal add .closing then wait for animationend then remove me">
<div class="modal-underlay" _="on click trigger closeModal"></div>
<div class="modal-content">
<h1>Modal Dialog</h1>
This is the modal content.
You can put anything here, like text, or a form, or an image.
<br>
<br>
<button class="btn danger" _="on click trigger closeModal">Close</button>
</div>
</div>
\`\`\`
## Custom Stylesheet
\`\`\`css
/***** MODAL DIALOG ****/
#modal {
/* Underlay covers entire screen. */
position: fixed;
top:0px;
bottom: 0px;
left:0px;
right:0px;
background-color:rgba(0,0,0,0.5);
z-index:1000;
/* Flexbox centers the .modal-content vertically and horizontally */
display:flex;
flex-direction:column;
align-items:center;
/* Animate when opening */
animation-name: fadeIn;
animation-duration:150ms;
animation-timing-function: ease;
}
#modal > .modal-underlay {
/* underlay takes up the entire viewport. This is only
required if you want to click to dismiss the popup */
position: absolute;
z-index: -1;
top:0px;
bottom:0px;
left: 0px;
right: 0px;
}
#modal > .modal-content {
/* Position visible dialog near the top of the window */
margin-top:10vh;
/* Sizing for visible dialog */
width:80%;
max-width:600px;
/* Display properties for visible dialog*/
border:solid 1px #999;
border-radius:8px;
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.3);
background-color:white;
padding:20px;
/* Animate when opening */
animation-name:zoomIn;
animation-duration:150ms;
animation-timing-function: ease;
}
#modal.closing {
/* Animate when closing */
animation-name: fadeOut;
animation-duration:150ms;
animation-timing-function: ease;
}
#modal.closing > .modal-content {
/* Animate when closing */
animation-name: zoomOut;
animation-duration:150ms;
animation-timing-function: ease;
}
@keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
@keyframes fadeOut {
0% {opacity: 1;}
100% {opacity: 0;}
}
@keyframes zoomIn {
0% {transform: scale(0.9);}
100% {transform: scale(1);}
}
@keyframes zoomOut {
0% {transform: scale(1);}
100% {transform: scale(0.9);}
}
\`\`\`
<script src="https://unpkg.com/htmx.org"></script>
<script src="https://unpkg.com/hyperscript.org"></script>
<script type="text/javascript">
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/modal", function(request){
return `
<div id="modal" _="on closeModal add .closing wait for animationend then remove me">
<div class="modal-underlay" _="on click trigger closeModal"></div>
<div class="modal-content">
<h1>Modal Dialog</h1>
This is the modal content.
You can put anything here, like text, or a form, or an image.
<br>
<br>
<button class="btn danger" _="on click trigger closeModal">Close</button>
</div>
</div>
`
});
</script>
<style>
/***** MODAL DIALOG ****/
#modal {
/* Underlay covers entire screen. */
position: fixed;
top:0px;
bottom: 0px;
left:0px;
right:0px;
background-color:rgba(0,0,0,0.5);
z-index:1000;
/* Flexbox centers the .modal-content vertically and horizontally */
display:flex;
flex-direction:column;
align-items:center;
/* Animate when opening */
animation-name: fadeIn;
animation-duration:150ms;
animation-timing-function: ease;
}
#modal > .modal-underlay {
/* underlay takes up the entire viewport. This is only
required if you want to click to dismiss the popup */
position: absolute;
z-index: -1;
top:0px;
bottom:0px;
left: 0px;
right: 0px;
}
#modal > .modal-content {
/* Position visible dialog near the top of the window */
margin-top:10vh;
/* Sizing for visible dialog */
width:80%;
max-width:600px;
/* Display properties for visible dialog*/
border:solid 1px #999;
border-radius:8px;
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.3);
background-color:white;
padding:20px;
/* Animate when opening */
animation-name:zoomIn;
animation-duration:150ms;
animation-timing-function: ease;
}
#modal.closing {
/* Animate when closing */
animation-name: fadeOut;
animation-duration:150ms;
animation-timing-function: ease;
}
#modal.closing > .modal-content {
/* Animate when closing */
animation-name: zoomOut;
animation-duration:150ms;
animation-timing-function: ease;
}
@keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
@keyframes fadeOut {
0% {opacity: 1;}
100% {opacity: 0;}
}
@keyframes zoomIn {
0% {transform: scale(0.9);}
100% {transform: scale(1);}
}
@keyframes zoomOut {
0% {transform: scale(1);}
100% {transform: scale(0.9);}
}
</style>
```
# examples/modal-uikit.md
```md
+++
title = "Modal Dialogs with UIKit"
template = "demo.html"
+++
Many CSS toolkits include styles (and Javascript) for creating modal dialog boxes.
This example shows how to use HTMX to display dynamic dialog using UIKit, and how to
trigger its animation styles with little or no Javascript.
We start with a button that triggers the dialog, along with a DIV at the bottom of your
markup where the dialog will be loaded:
This is an example of using HTMX to remotely load modal dialogs using UIKit. In this example we will use
[Hyperscript](https://hyperscript.org) to demonstrate how cleanly that scripting language allows you to
glue htmx and other libraries together.
\`\`\`html
<button
id="showButton"
hx-get="/uikit-modal.html"
hx-target="#modals-here"
class="uk-button uk-button-primary"
_="on htmx:afterOnLoad wait 10ms then add .uk-open to #modal">Open Modal</button>
<div id="modals-here"></div>
\`\`\`
This button uses a `GET` request to `/uikit-modal.html` when this button is clicked. The
contents of this file will be added to the DOM underneath the `#modals-here` DIV.
Rather than using the standard UIKit Javascript library we are using a bit of Hyperscript,
which triggers UIKit's smooth animations. It is delayed by 10ms so that UIKit's animations
will run correctly.
Finally, the server responds with a slightly modified version of UIKit's standard modal
\`\`\`html
<div id="modal" class="uk-modal" style="display:block;">
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Modal Dialog</h2>
<p>This modal dialog was loaded dynamically by HTMX.</p>
<form _="on submit take .uk-open from #modal">
<div class="uk-margin">
<input class="uk-input" placeholder="What is Your Name?">
</div>
<button type="button" class="uk-button uk-button-primary">Save Changes</button>
<button
id="cancelButton"
type="button"
class="uk-button uk-button-default"
_="on click take .uk-open from #modal wait 200ms then remove #modal">Close</button>
</form>
</div>
</div>
\`\`\`
Hyperscript on the button and the form trigger animations when this dialog is completed
or canceled. If you didn't use this Hyperscript, the modals will still work but UIKit's
fade in animations will not be triggered.
You can, of course, use JavaScript rather than Hyperscript for this work, it's just a lot more code:
\`\`\`javascript
// This triggers the fade-in animation when a modal dialog is loaded and displayed
window.document.getElementById("showButton").addEventListener("htmx:afterOnLoad", function() {
setTimeout(function(){
window.document.getElementById("modal").classList.add("uk-open")
}, 10)
})
// This triggers the fade-out animation when the modal is closed.
window.document.getElementById("cancelButton").addEventListener("click", function() {
window.document.getElementById("modal").classList.remove("uk-open")
setTimeout(function(){
window.document.getElementById("modals-here").innerHTML = ""
,200
})
})
\`\`\`
<div id="modals-here"></div>
{{ demoenv() }}
<style>
@import "https://cdnjs.cloudflare.com/ajax/libs/uikit/3.5.9/css/uikit-core.min.css";
</style>
<script src="https://unpkg.com/hyperscript.org"></script>
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params) {
return `
<button
class="uk-button uk-button-primary"
hx-get="/modal"
hx-trigger="click"
hx-target="#modals-here"
_="on htmx:afterOnLoad wait 10ms then add .uk-open to #modal">Show Modal Dialog</button>`
})
onGet("/modal", function(request, params){
return `
<div id="modal" class="uk-modal" style="display:block;">
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Modal Dialog</h2>
<p>This modal dialog was loaded dynamically by HTMX. You can put any server request here and you don't (necessarily) need to use the UIKit Javascript file to make it work</p>
<form _="on submit take .uk-open from #modal">
<div class="uk-margin">
<input class="uk-input" placeholder="What is Your Name?">
</div>
<div class="uk-margin">
<input class="uk-input" placeholder="What is Your Quest?">
</div>
<div class="uk-margin">
<input class="uk-input" placeholder="What is Your Favorite Color?">
</div>
<button type="button" class="uk-button uk-button-primary" _="on click call alert('submit to server and close dialog.')">Save Changes</button>
<button type="button" class="uk-button uk-button-default" _="on click take .uk-open from #modal wait 200ms then remove #modal">Close</button>
</form>
</div>
</div>`
});
</script>
```
# examples/move-before/details.md
```md
+++
title = "Experimental moveBefore() Support"
insert_anchor_links = "left"
+++
### Demo
Note that the video kept playing, despite the fact it is now a child of a `figure` element. And you can click back
and the video should continue to work as well. I enjoy flipping back and forth.
### Rick-Rolling
<figure>
<iframe hx-preserve="true" id="rick-roll" width="617" height="351" src="https://www.youtube.com/embed/dQw4w9WgXcQ"
title="Rick Astley - Never Gonna Give You Up (Official Music Video)" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<figcaption>A Classic Rick Roll</figcaption>
</figure>
#### Background
Rickrolling or a Rickroll is an Internet meme involving the unexpected appearance of the music video to the 1987 hit
song "Never Gonna Give You Up", performed by English singer Rick Astley. The aforementioned video has over 1.5 billion
views on YouTube. The meme is a type of bait and switch, usually using a disguised hyperlink that leads to the music
video. When one clicks on a seemingly unrelated link, the site with the music video loads instead of what was expected,
and they have been "Rickrolled". The meme has also extended to using the song's lyrics, or singing it, in unexpected
contexts. Astley himself has also been Rickrolled on several occasions.
The meme grew out of a similar bait-and-switch trick called "duck rolling" that was popular on the 4chan website in
2006. The video bait-and-switch trick grew popular on 4chan by 2007 during April Fools' Day and spread to other Internet
sites later that year. The meme gained mainstream attention in 2008 through several publicised events, particularly when
YouTube used it on its 2008 April Fools' Day event.
Astley, who had only returned to performing after a 10-year hiatus, was initially hesitant about using his newfound
popularity from the meme to further his career but accepted the fame by Rickrolling the 2008 Macy's Thanksgiving Day
Parade with a surprise performance of the song. Since then, Astley has seen his performance career revitalized by the
meme's popularity, and Rickrolling saw a massive resurgence online in the early 2020s.
```
# examples/progress-bar.md
```md
+++
title = "Progress Bar"
template = "demo.html"
+++
This example shows how to implement a smoothly scrolling progress bar.
We start with an initial state with a button that issues a `POST` to `/start` to begin the job:
\`\`\`html
<div hx-target="this" hx-swap="outerHTML">
<h3>Start Progress</h3>
<button class="btn primary" hx-post="/start">
Start Job
</button>
</div>
\`\`\`
This div is then replaced with a new div containing status and a progress bar that reloads itself every 600ms:
\`\`\`html
<div hx-trigger="done" hx-get="/job" hx-swap="outerHTML" hx-target="this">
<h3 role="status" id="pblabel" tabindex="-1" autofocus>Running</h3>
<div
hx-get="/job/progress"
hx-trigger="every 600ms"
hx-target="this"
hx-swap="innerHTML">
<div class="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" aria-labelledby="pblabel">
<div id="pb" class="progress-bar" style="width:0%">
</div>
</div>
</div>
\`\`\`
This progress bar is updated every 600 milliseconds, with the "width" style attribute and `aria-valuenow` attributed set to current progress value.
Because there is an id on the progress bar div, htmx will smoothly transition between requests by settling the
style attribute into its new value. This, when coupled with CSS transitions, makes the visual transition continuous
rather than jumpy.
Finally, when the process is complete, a server returns `HX-Trigger: done` header, which triggers an update of the UI to "Complete" state
with a restart button added to the UI (we are using the [`class-tools`](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/class-tools/README.md) extension in this example to add fade-in effect on the button):
\`\`\`html
<div hx-trigger="done" hx-get="/job" hx-swap="outerHTML" hx-target="this">
<h3 role="status" id="pblabel" tabindex="-1" autofocus>Complete</h3>
<div
hx-get="/job/progress"
hx-trigger="none"
hx-target="this"
hx-swap="innerHTML">
<div class="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="122" aria-labelledby="pblabel">
<div id="pb" class="progress-bar" style="width:122%">
</div>
</div>
</div>
<button id="restart-btn" class="btn primary" hx-post="/start" classes="add show:600ms">
Restart Job
</button>
</div>
\`\`\`
This example uses styling cribbed from the bootstrap progress bar:
\`\`\`css
.progress {
height: 20px;
margin-bottom: 20px;
overflow: hidden;
background-color: #f5f5f5;
border-radius: 4px;
box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
}
.progress-bar {
float: left;
width: 0%;
height: 100%;
font-size: 12px;
line-height: 20px;
color: #fff;
text-align: center;
background-color: #337ab7;
-webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}
\`\`\`
{{ demoenv() }}
<style>
.progress {
height: 20px;
margin-bottom: 20px;
overflow: hidden;
background-color: #f5f5f5;
border-radius: 4px;
box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
}
.progress-bar {
float: left;
width: 0%;
height: 100%;
font-size: 12px;
line-height: 20px;
color: #fff;
text-align: center;
background-color: #337ab7;
-webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}
#restart-btn {
opacity:0;
}
#restart-btn.show {
opacity:1;
transition: opacity 100ms ease-in;
}
</style>
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params){
return startButton("Start Progress");
});
onPost("/start", function(request, params){
var job = jobManager.start();
return jobStatusTemplate(job);
});
onGet("/job", function(request, params){
var job = jobManager.currentProcess();
return jobStatusTemplate(job);
});
onGet("/job/progress", function(request, params, responseHeaders){
var job = jobManager.currentProcess();
if (job.complete) {
responseHeaders["HX-Trigger"] = "done";
}
return jobProgressTemplate(job);
});
// templates
function startButton(message) {
return `<div hx-target="this" hx-swap="outerHTML">
<h3>${message}</h3>
<button class="btn primary" hx-post="/start">
Start Job
</button>
</div>`;
}
function jobProgressTemplate(job) {
return `<div class="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${job.percentComplete}" aria-labelledby="pblabel">
<div id="pb" class="progress-bar" style="width:${job.percentComplete}%">
</div>
</div>`
}
function jobStatusTemplate(job) {
return `<div hx-trigger="done" hx-get="/job" hx-swap="outerHTML" hx-target="this">
<h3 role="status" id="pblabel" tabindex="-1" autofocus>${job.complete ? "Complete" : "Running"}</h3>
<div
hx-get="/job/progress"
hx-trigger="${job.complete ? 'none' : 'every 600ms'}"
hx-target="this"
hx-swap="innerHTML">
${jobProgressTemplate(job)}
</div>
${restartButton(job)}`;
}
function restartButton(job) {
if(job.complete){
return `
<button id="restart-btn" class="btn primary" hx-post="/start" classes="add show:600ms">
Restart Job
</button>`
} else {
return "";
}
}
var jobManager = (function(){
var currentProcess = null;
return {
start : function() {
currentProcess = {
complete : false,
percentComplete : 0
}
return currentProcess;
},
currentProcess : function() {
currentProcess.percentComplete += Math.min(100, Math.floor(33 * Math.random())); // simulate progress
currentProcess.complete = currentProcess.percentComplete >= 100;
return currentProcess;
}
}
})();
</script>
```
# examples/reset-user-input.md
```md
+++
title = "Reset user input"
template = "demo.html"
+++
This example shows how to easily reset user inputs using [`hx-on`](@/attributes/hx-on.md),
allowing users to make multiple requests without having to manually delete their previous inputs.
The inline script will run on the [`afterRequest`](@/events.md#htmx:afterRequest) event and ensures
that the form will reset to its initial state as long as the response has a 20x status code:
\`\`\`html
<form hx-post="/note"
hx-target="#notes"
hx-swap="afterbegin"
hx-on::after-request="if(event.detail.successful) this.reset()">
<div class="form-group">
<label>Add a note</label>
<input type="text" name="note-text" placeholder="blank canvas">
</div>
<button class="btn">Add</button>
</form>
<ul id="notes"><!-- Response will go here --></ul>
\`\`\`
The `reset()` method is only available on `form` elements.
For other elements, the input value can explicitly selected and reset to a default to achieve the same result.
The following code is functionally equivalent:
\`\`\`html
<div>
<label>Add a note</label>
<input id="note-input" type="text" name="note-text" placeholder="blank canvas">
</div>
<button class="btn primary"
hx-post="/note"
hx-target="#note"
hx-swap="afterbegin"
hx-include="#note-input"
hx-on::after-request="if(event.detail.successful)
document.getElementById('note-input').value = ''">
Add
</button>
<ul id="notes"><!-- Response will go here --></ul>
\`\`\`
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request) {
return formTemplate();
})
onPost("/note", function(request, params) {
var note = params['note-text'];
if (note) {
return `<li>${note}</li>`;
}
})
// templates
function formTemplate() {
return `
<form hx-post="/note" hx-target="#notes" hx-swap="afterbegin" hx-on::after-request="if(event.detail.successful) this.reset()">
<div class="form-group">
<label>Add a note</label>
<input type="text" name="note-text" placeholder="blank canvas">
</div>
<button class="btn primary">Add</button>
</form>
<ul id="notes"> </ul>`;
}
</script>
```
# examples/sortable.md
```md
+++
title = "Sortable"
template = "demo.html"
+++
In this example we show how to integrate the [Sortable](https://sortablejs.github.io/Sortable/)
javascript library with htmx.
To begin we initialize the `.sortable` class with the `Sortable` javascript library:
\`\`\`js
htmx.onLoad(function(content) {
var sortables = content.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
var sortableInstance = new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class',
// Make the `.htmx-indicator` unsortable
filter: ".htmx-indicator",
onMove: function (evt) {
return evt.related.className.indexOf('htmx-indicator') === -1;
},
// Disable sorting on the `end` event
onEnd: function (evt) {
this.option("disabled", true);
}
});
// Re-enable sorting on the `htmx:afterSwap` event
sortable.addEventListener("htmx:afterSwap", function() {
sortableInstance.option("disabled", false);
});
}
})
\`\`\`
Next, we create a form that has some sortable divs within it, and we trigger an ajax request on the `end` event, fired
by Sortable.js:
\`\`\`html
<form class="sortable" hx-post="/items" hx-trigger="end">
<div class="htmx-indicator">Updating...</div>
<div><input type='hidden' name='item' value='1'/>Item 1</div>
<div><input type='hidden' name='item' value='2'/>Item 2</div>
<div><input type='hidden' name='item' value='3'/>Item 3</div>
<div><input type='hidden' name='item' value='4'/>Item 4</div>
<div><input type='hidden' name='item' value='5'/>Item 5</div>
</form>
\`\`\`
Note that each div has a hidden input inside of it that specifies the item id for that row.
When the list is reordered via the Sortable.js drag-and-drop, the `end` event will be fired. htmx will then post
the item ids in the new order to `/items`, to be persisted by the server.
That's it!
{{ demoenv() }}
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
htmx.onLoad(function(content) {
var sortables = content.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
var sortableInstance = new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class',
// Make the `.htmx-indicator` unsortable
filter: ".htmx-indicator",
onMove: function (evt) {
return evt.related.className.indexOf('htmx-indicator') === -1;
},
// Disable sorting on the `end` event
onEnd: function (evt) {
this.option("disabled", true);
}
});
// Re-enable sorting on the `htmx:afterSwap` event
sortable.addEventListener("htmx:afterSwap", function() {
sortableInstance.option("disabled", false);
});
}
})
var listItems = [1, 2, 3, 4, 5]
// routes
init("/demo", function(request, params){
return '<form id="example1" class="list-group col sortable" hx-post="/items" hx-trigger="end">' +
listContents()
+ "\n</form>";
});
onPost("/items", function (request, params) {
console.log(params);
listItems = params.item;
return listContents();
});
// templates
function listContents() {
return '<div class="htmx-indicator" style="cursor: default">Updating...</div>' + listItems.map(function(val) {
return ` <div style="border:1px solid #DEDEDE; padding:12px; margin: 8px; width:200px; cursor: grab" ondrag="this.style.cursor = 'grabbing'" ><input type="hidden" name="item" value="` + val + `"/> Item ` + val + `</div>`;
}).join("\n");
}
</script>
```
# examples/tabs-hateoas.md
```md
+++
title = "Tabs (Using HATEOAS)"
template = "demo.html"
+++
This example shows how easy it is to implement tabs using htmx. Following the principle of [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS), the selected tab is a part of the application state. Therefore, to display and select tabs in your application, simply include the tab markup in the returned HTML. If this does not suit your application server design, you can also use a little bit of [JavaScript to select tabs instead](@/examples/tabs-javascript.md).
## Example Code (Main Page)
The main page simply includes the following HTML to load the initial tab into the DOM.
\`\`\`html
<div id="tabs" hx-get="/tab1" hx-trigger="load delay:100ms" hx-target="#tabs" hx-swap="innerHTML"></div>
\`\`\`
## Example Code (Each Tab)
Subsequent tab pages display all tabs and highlight the selected one accordingly.
\`\`\`html
<div class="tab-list" role="tablist">
<button hx-get="/tab1" class="selected" role="tab" aria-selected="true" aria-controls="tab-content">Tab 1</button>
<button hx-get="/tab2" role="tab" aria-selected="false" aria-controls="tab-content">Tab 2</button>
<button hx-get="/tab3" role="tab" aria-selected="false" aria-controls="tab-content">Tab 3</button>
</div>
<div id="tab-content" role="tabpanel" class="tab-content">
Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
Polaroid duis occaecat narwhal small batch food truck.
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.
</div>
\`\`\`
{{ demoenv() }}
<div id="tabs" hx-target="this" hx-swap="innerHTML">
<div class="tab-list" role="tablist">
<button hx-get="/tab1" class="selected" role="tab" aria-selected="true" aria-controls="tab-content">Tab 1</button>
<button hx-get="/tab2" role="tab" aria-selected="false" aria-controls="tab-content">Tab 2</button>
<button hx-get="/tab3" role="tab" aria-selected="false" aria-controls="tab-content">Tab 3</button>
</div>
<div id="tab-content" role="tabpanel" class="tab-content">
Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
Polaroid duis occaecat narwhal small batch food truck.
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.
</div>
</div>
<script>
onGet("/tab1", function() {
return `
<div class="tab-list" role="tablist">
<button hx-get="/tab1" class="selected" aria-selected="true" autofocus role="tab" aria-controls="tab-content">Tab 1</button>
<button hx-get="/tab2" role="tab" aria-selected="false" aria-controls="tab-content">Tab 2</button>
<button hx-get="/tab3" role="tab" aria-selected="false" aria-controls="tab-content">Tab 3</button>
</div>
<div id="tab-content" role="tabpanel" class="tab-content">
Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
Polaroid duis occaecat narwhal small batch food truck.
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.
</div>`
})
onGet("/tab2", function() {
return `
<div class="tab-list" role="tablist">
<button hx-get="/tab1" role="tab" aria-selected="false" aria-controls="tab-content">Tab 1</button>
<button hx-get="/tab2" class="selected" aria-selected="true" autofocus role="tab" aria-controls="tab-content">Tab 2</button>
<button hx-get="/tab3" role="tab" aria-selected="false" aria-controls="tab-content">Tab 3</button>
</div>
<div id="tab-content" role="tabpanel" class="tab-content">
Kitsch fanny pack yr, farm-to-table cardigan cillum commodo reprehenderit plaid dolore cronut meditation.
Tattooed polaroid veniam, anim id cornhole hashtag sed forage.
Microdosing pug kitsch enim, kombucha pour-over sed irony forage live-edge.
Vexillologist eu nulla trust fund, street art blue bottle selvage raw denim.
Dolore nulla do readymade, est subway tile affogato hammock 8-bit.
Godard elit offal pariatur you probably haven't heard of them post-ironic.
Prism street art cray salvia.
</div>`
})
onGet("/tab3", function() {
return `
<div class="tab-list" role="tablist">
<button hx-get="/tab1" role="tab" aria-selected="false" aria-controls="tab-content">Tab 1</button>
<button hx-get="/tab2" role="tab" aria-selected="false" aria-controls="tab-content">Tab 2</button>
<button hx-get="/tab3" class="selected" aria-selected="true" autofocus role="tab" aria-controls="tab-content">Tab 3</button>
</div>
<div id="tab-content" role="tabpanel" class="tab-content">
Aute chia marfa echo park tote bag hammock mollit artisan listicle direct trade.
Raw denim flexitarian eu godard etsy.
Poke tbh la croix put a bird on it fixie polaroid aute cred air plant four loko gastropub swag non brunch.
Iceland fanny pack tumeric magna activated charcoal bitters palo santo laboris quis consectetur cupidatat portland aliquip venmo.
</div>`
})
</script>
<style>
#demo-canvas {
display:none;
}
#tabs > .tab-list button {
border: none;
display: inline-block;
padding: 5px 10px;
cursor:pointer;
background-color: transparent;
color: var(--textColor);
border: solid 3px rgba(0,0,0,0);
border-bottom: solid 3px #eee;
}
#tabs > .tab-list button:hover {
color: var(--midBlue);
}
#tabs > .tab-list button.selected {
border: solid 3px var(--midBlue);
}
#tabs > .tab-content {
padding:10px;
margin-bottom:100px;
}
</style>
```
# examples/tabs-javascript.md
```md
+++
title = "Tabs (Using JavaScript)"
template = "demo.html"
+++
This example shows how to load tab contents using htmx, and to select the "active" tab using Javascript. This reduces
some duplication by offloading some of the work of re-rendering the tab HTML from your application server to your
clients' browsers.
You may also consider [a more idiomatic approach](@/examples/tabs-hateoas.md) that follows the principle of [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS).
## Example Code
The HTML below displays a list of tabs, with added HTMX to dynamically load each tab pane from the server. A simple
JavaScript event handler uses the [`take` function](https://hyperscript.org/commands/take/) to switch the selected tab
when the content is swapped into the DOM.
\`\`\`html
<div id="tabs" hx-target="#tab-contents" role="tablist"
hx-on:htmx-after-on-load="let currentTab = document.querySelector('[aria-selected=true]');
currentTab.setAttribute('aria-selected', 'false')
currentTab.classList.remove('selected')
let newTab = event.target
newTab.setAttribute('aria-selected', 'true')
newTab.classList.add('selected')">
<button role="tab" aria-controls="tab-contents" aria-selected="true" hx-get="/tab1" class="selected">Tab 1</button>
<button role="tab" aria-controls="tab-contents" aria-selected="false" hx-get="/tab2">Tab 2</button>
<button role="tab" aria-controls="tab-contents" aria-selected="false" hx-get="/tab3">Tab 3</button>
</div>
<div id="tab-contents" role="tabpanel" hx-get="/tab1" hx-trigger="load"></div>
\`\`\`
{{ demoenv() }}
<div id="tabs" hx-target="#tab-contents" role="tablist"
hx-on:htmx-after-on-load="console.log(event)
let currentTab = document.querySelector('[aria-selected=true]');
currentTab.setAttribute('aria-selected', 'false')
currentTab.classList.remove('selected')
let newTab = event.target
newTab.setAttribute('aria-selected', 'true')
newTab.classList.add('selected')">
<button role="tab" aria-controls="tab-contents" aria-selected="true" hx-get="/tab1" class="selected">Tab 1</button>
<button role="tab" aria-controls="tab-contents" aria-selected="false" hx-get="/tab2">Tab 2</button>
<button role="tab" aria-controls="tab-contents" aria-selected="false" hx-get="/tab3">Tab 3</button>
</div>
<div id="tab-contents" role="tabpanel" hx-get="/tab1" hx-trigger="load"></div>
<script src="https://unpkg.com/hyperscript.org"></script>
<script>
onGet("/tab1", function() {
return `
<p>Commodo normcore truffaut VHS duis gluten-free keffiyeh iPhone taxidermy godard ramps anim pour-over.
Pitchfork vegan mollit umami quinoa aute aliquip kinfolk eiusmod live-edge cardigan ipsum locavore.
Polaroid duis occaecat narwhal small batch food truck.
PBR&B venmo shaman small batch you probably haven't heard of them hot chicken readymade.
Enim tousled cliche woke, typewriter single-origin coffee hella culpa.
Art party readymade 90's, asymmetrical hell of fingerstache ipsum.</p>
`});
onGet("/tab2", function() {
return `
<p>Kitsch fanny pack yr, farm-to-table cardigan cillum commodo reprehenderit plaid dolore cronut meditation.
Tattooed polaroid veniam, anim id cornhole hashtag sed forage.
Microdosing pug kitsch enim, kombucha pour-over sed irony forage live-edge.
Vexillologist eu nulla trust fund, street art blue bottle selvage raw denim.
Dolore nulla do readymade, est subway tile affogato hammock 8-bit.
Godard elit offal pariatur you probably haven't heard of them post-ironic.
Prism street art cray salvia.</p>
`
});
onGet("/tab3", function() {
return `
<p>Aute chia marfa echo park tote bag hammock mollit artisan listicle direct trade.
Raw denim flexitarian eu godard etsy.
Poke tbh la croix put a bird on it fixie polaroid aute cred air plant four loko gastropub swag non brunch.
Iceland fanny pack tumeric magna activated charcoal bitters palo santo laboris quis consectetur cupidatat portland aliquip venmo.</p>
`
});
</script>
<style>
#demo-canvas {
display:none;
}
#tabs {
}
#tabs > button {
border: none;
display: inline-block;
padding: 5px 10px;
cursor:pointer;
background-color: transparent;
border: solid 3px rgba(0,0,0,0);
border-bottom: solid 3px #eee;
}
#tabs > button:hover {
color: var(--midBlue);
}
#tabs > button.selected {
border: solid 3px var(--midBlue);
}
#tab-contents {
padding:10px;
}
</style>
```
# examples/update-other-content.md
```md
+++
title = "Updating Other Content"
template = "demo.html"
+++
A question that often comes up when people are first working with htmx is:
> "I need to update other content on the screen. How do I do this?"
There are multiple ways to do so, and in this example we will walk you through some of them.
We'll use the following basic UI to discuss this concept: a simple table of contacts, and a form below it
to add new contacts to the table using [hx-post](@/attributes/hx-post.md).
\`\`\`html
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
\`\`\`
The problem here is that when you submit a new contact in the form, you want the contact table above to refresh and
include the contact that was just added by the form.
What solutions do we have?
## Solution 1: Expand the Target {#expand}
The easiest solution here is to "expand the target" of the form to enclose both the table *and* the form. For example,
you could wrap the whole thing in a `div` and then target that `div` in the form:
\`\`\`html
<div id="table-and-form">
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts" hx-target="#table-and-form">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
</div>
\`\`\`
Note that we are targeting the enclosing div using the [hx-target](@/attributes/hx-target.md) attribute. You would need
to render both the table and the form in the response to the `POST` to `/contacts`.
This is a simple and reliable approach, although it might not feel particularly elegant.
## Solution 2: Out of Band Responses {#oob}
A more sophisticated approach to this problem would use [out of band swaps](@/attributes/hx-swap-oob.md) to swap in
updated content to the DOM.
Using this approach, the HTML doesn't need to change from the original setup at all:
\`\`\`html
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
\`\`\`
Instead of modifying something on the front end, in your response to the `POST` to `/contacts` you would include some additional content:
\`\`\`html
<tbody hx-swap-oob="beforeend:#contacts-table">
<tr>
<td>Joe Smith</td>
<td>joe@smith.com</td>
</tr>
</tbody>
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
\`\`\`
This content uses the [hx-swap-oob](@/attributes/hx-swap-oob.md) attribute to append itself to the `#contacts-table`, updating
the table after a contact is added successfully.
## Solution 3: Triggering Events {#events}
An even more sophisticated approach would be to trigger a client side event when a successful contact is created and
then listen for that event on the table, causing the table to refresh.
\`\`\`html
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table" hx-get="/contacts/table" hx-trigger="newContact from:body">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
\`\`\`
We have added a new end-point `/contacts/table` that re-renders the contacts table. Our trigger for this request
is a custom event, `newContact`. We listen for this event on the `body` because when it
is triggered by the response to the form, it will end up hitting the body due to event bubbling.
When a successful contact creation occurs during a POST to `/contacts`, the response includes
an [HX-Trigger](@/headers/hx-trigger.md) response header that looks like this:
\`\`\`txt
HX-Trigger:newContact
\`\`\`
This will trigger the table to issue a `GET` to `/contacts/table` and this will render the newly added contact row
(in addition to the rest of the table.)
Very clean, event driven programming!
## Solution 4: Using the Path Dependencies Extension {#path-deps}
A final approach is to use REST-ful path dependencies to refresh the table. Intercooler.js, the predecessor
to htmx, had [path-based dependencies](https://intercoolerjs.org/docs.html#dependencies) integrated into the
library.
htmx dropped this as a core feature, but supports an extension, [path deps](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md), that gives you
similar functionality.
Updating our example to use the extension would involve loading the extension javascript and then
annotating our HTML like so:
\`\`\`html
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table" hx-get="/contacts/table" hx-ext="path-deps" hx-trigger="path-deps" path-deps="/contacts">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
\`\`\`
Now, when the form posts to the `/contacts` URL, the `path-deps` extension will detect that and trigger an `path-deps`
event on the contacts table, therefore triggering a request.
The advantage here is that you don't need to do anything fancy with response headers. The downside is that a request
will be issued on every `POST`, even if a contact was not successfully created.
## Which should I use?
Generally I would recommend the first approach, expanding your target, especially if the elements that need to be
updated are reasonably close to one another in the DOM. It is simple and reliable.
After that, I would say it is a tossup between the custom event and an OOB swap approaches. I would lean towards the custom event
approach because I like event-oriented systems, but that's a personal preference. Which one you choose should be dictated by your
own software engineering tastes and which of the two matches up better with your server side technology of choice.
Finally, the path-deps approach is interesting, and if it fits well with your mental model and overall system architecture,
it can be a fun way to avoid explicit refreshing. I would look at it last, however, unless the concept really grabs
you.
```
# examples/value-select.md
```md
+++
title = "Cascading Selects"
template = "demo.html"
+++
In this example we show how to make the values in one `select` depend on the value selected in another `select`.
To begin we start with a default value for the `make` select: Audi. We render the `model` select for this make. We
then have the `make` select trigger a `GET` to `/models` to retrieve the models options and target the `models` select.
Here is the code:
\`\`\`html
<div>
<label >Make</label>
<select name="make" hx-get="/models" hx-target="#models" hx-indicator=".htmx-indicator">
<option value="audi">Audi</option>
<option value="toyota">Toyota</option>
<option value="bmw">BMW</option>
</select>
</div>
<div>
<label>Model</label>
<select id="models" name="model">
<option value="a1">A1</option>
...
</select>
<img class="htmx-indicator" width="20" src="/img/bars.svg">
</div>
\`\`\`
When a request is made to the `/models` end point, we return the models for that make:
\`\`\`html
<option value='325i'>325i</option>
<option value='325ix'>325ix</option>
<option value='X5'>X5</option>
\`\`\`
And they become available in the `model` select.
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// routes
init("/demo", function(request, params){
return formTemplate();
});
onGet(/models.*/, function (request, params) {
var make = dataStore.findMake(params['make']);
return modelOptionsTemplate(make['models']);
});
// templates
function formTemplate() {
return ` <h3>Pick A Make/Model</h3>
<form>
<div>
<label >Make</label>
<select name="make" hx-get="/models" hx-target="#models" hx-indicator=".htmx-indicator">
<option value="audi">Audi</option>
<option value="toyota">Toyota</option>
<option value="bmw">BMW</option>
</select>
</div>
<div>
<label>Model</label>
<select id="models" name="model">
<option value="a1">A1</option>
<option value="a3">A3</option>
<option value="a6">A6</option>
</select>
<img class="htmx-indicator" width="20" src="/img/bars.svg">
</div>
</form>`;
}
function modelOptionsTemplate(make) {
return make.map(function(val) {
return "<option value='" + val + "'>" + val +"</option>";
}).join("\n");
}
var dataStore = function(){
var data = {
audi : { models : ["A1", "A4", "A6"] },
toyota : { models : ["Landcruiser", "Tacoma", "Yaris"] },
bmw : { models : ["325i", "325ix", "X5"] }
};
return {
findMake : function(make) {
return data[make];
}
}
}()
</script>
```
# examples/web-components.md
```md
+++
title = "Web Components"
template = "demo.html"
+++
This example shows how to integrate HTMX with web components, allowing it to be used inside of shadow DOM.
By default, HTMX doesn't know anything about your web components, and won't see anything inside their shadow
DOM. Because of this, you'll need to manually tell HTMX about your component's shadow DOM by using
[`htmx.process`](https://htmx.org/api/#process).
\`\`\`js
customElements.define('my-component', class MyComponent extends HTMLElement {
// This method runs when your custom element is added to the page
connectedCallback() {
const root = this.attachShadow({ mode: 'closed' })
root.innerHTML = `
<button hx-get="/my-component-clicked" hx-target="next div">Click me!</button>
<div></div>
`
htmx.process(root) // Tell HTMX about this component's shadow DOM
}
})
\`\`\`
Once you've told HTMX about your component's shadow DOM, most things should work as expected. However, note
that selectors such as in `hx-target` will only see elements inside the same shadow DOM - if you need to
access things outside of your web components, you can use one of the following options:
- `host`: Selects the element hosting the current shadow DOM
- `global`: If used as a prefix, selects from the main document instead of the current shadow DOM
The same principles generally apply to web components that don't use shadow DOM as well; while selectors
won't be encapsulated like with shadow DOM, you'll still have to point HTMX to your component's content by
calling `htmx.process`.
{{ demoenv() }}
<script>
//=========================================================================
// Fake Server Side Code
//=========================================================================
// data
let timesClicked = 0
customElements.define('my-component', class MyComponent extends HTMLElement {
// This method runs when your custom element is added to the page
connectedCallback() {
const root = this.attachShadow({ mode: 'closed' })
root.innerHTML = `
<button hx-get="/my-component-clicked" hx-target="next div">Click me!</button>
<div></div>
`
htmx.process(root) // Tell HTMX about this component's shadow DOM
}
})
// routes
init('/demo', function() {
return `<my-component></my-component>`
})
onGet('/my-component-clicked', function() {
return `<p>Clicked ${++timesClicked} time${timesClicked > 1 ? 's' : ''}!</p>`
})
</script>
```
# extensions/building.md
```md
+++
title = "Building htmx Extensions"
+++
To define an extension you call the `htmx.defineExtension()` function:
\`\`\`html
<script>
htmx.defineExtension('my-ext', {
onEvent : function(name, evt) {
console.log("Fired event: " + name, evt);
}
})
</script>
\`\`\`
Typically, this is done in a stand-alone javascript file, rather than in an inline `script` tag.
Extensions should have names that are dash separated and that are reasonably short and descriptive.
Extensions can override the following default extension points to add or change functionality:
\`\`\`javascript
{
init: function(api) {return null;},
getSelectors: function() {return null;},
onEvent : function(name, evt) {return true;},
transformResponse : function(text, xhr, elt) {return text;},
isInlineSwap : function(swapStyle) {return false;},
handleSwap : function(swapStyle, target, fragment, settleInfo) {return false;},
encodeParameters : function(xhr, parameters, elt) {return null;}
}
\`\`\`
```
# extensions/head-support.md
```md
+++
title = "htmx Head Tag Support Extension"
+++
The `head-support` extension adds support for [head tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
in responses to htmx requests.
htmx began as a library focused on _partial replacement_ of HTML within the `body` tag. As such, merging additional
information such as the head tag was not a focus of the library. (This is in contrast with, for example, TurboLinks,
which was focused on merging entire web pages retrieved via AJAX into the browser.)
The [`hx-boost`](@/attributes/hx-boost.md) attribute moved htmx closer to this world of full HTML-document support &
support for extracting the `title` tag out of head elements was eventually added, but full head tag support has never been
a feature of the library. This extension addresses that shortcoming.
## Installing
The fastest way to install `head-support` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-head-support@2.0.2" integrity="sha384-cvMqHzjCJsOHgGuyB3sWXaUSv/Krm0BdzjuI1rtkjCbL1l1oHJx+cHyVRJhyuEz0" crossorigin="anonymous"></script>
</head>
<body hx-ext="head-support">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-head-support/dist/head-support.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `head-support` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-head-support`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `head-support` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-head-support
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-head-support/dist/head-support.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-head-support` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-head-support`;
\`\`\`
## Usage
\`\`\`html
<body hx-ext="head-support">
...
</body>
\`\`\`
With this installed, all responses that htmx receives that contain a `head` tag in them (even if they are not complete
HTML documents with a root `<html>` element) will be processed.
How the head tag is handled depends on the type of htmx request.
If the htmx request is from a boosted element, then the following merge algorithm is used:
* Elements that exist in the current head as exact textual matches will be left in place
* Elements that do not exist in the current head will be added at the end of the head tag
* Elements that exist in the current head, but not in the new head will be removed from the head
If the htmx request is from a non-boosted element, then all content will be _appended_ to the existing head element.
If you wish to override this behavior in either case, you can place the `hx-head` attribute on the new `<head>` tag,
with either of the following two values:
* `merge` - follow the merging algorithm outlined above
* `append` - append the elements to the existing head
### Controlling Merge Behavior
Beyond this, you may also control merging behavior of individual elements with the following attributes:
* If you place `hx-head="re-eval"` on a head element, it will be re-added (removed and appended) to the head tag on every
request, even if it already exists. This can be useful to execute a script on every htmx request, for example.
* If you place `hx-preserve="true"` on an element, it will never be removed from the head
### Example
As an example, consider the following head tag in an existing document:
\`\`\`html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<link rel="stylesheet" href="/css/site1.css">
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
</head>
\`\`\`
If htmx receives a request containing this new head tag:
\`\`\`html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<link rel="stylesheet" href="/css/site2.css">
<script src="/js/script2.js"></script>
<script src="/js/script3.js"></script>
</head>
\`\`\`
Then the following operations will occur:
* `<link rel="stylesheet" href="https://the.missing.style">` will be left alone
* `<link rel="stylesheet" href="/css/site1.css">` will be removed from the head
* `<link rel="stylesheet" href="/css/site2.css">` will be added to the head
* `<script src="/js/script1.js"></script>` will be removed from the head
* `<script src="/js/script2.js"></script>` will be left alone
* `<script src="/js/script3.js"></script>` will be added to the head
The final head element will look like this:
\`\`\`html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<script src="/js/script2.js"></script>
<link rel="stylesheet" href="/css/site2.css">
<script src="/js/script3.js"></script>
</head>
\`\`\`
## Events
This extension triggers the following events:
* `htmx:removingHeadElement` - triggered when a head element is about to be removed, with the element being removed
available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be removed.
* `htmx:addingHeadElement` - triggered when a head element is about to be added, with the element being added
available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be added.
* `htmx:afterHeadMerge` - triggered after a head tag merge has occurred, with the following values available in the event `detail`:
* `added` - added head elements
* `kept` - kept head elements
* `removed` - removed head elements
* `htmx:beforeHeadMerge` - triggered before a head merge occurs
```
# extensions/htmx-1-compat.md
```md
+++
title = "htmx 1.x Compatibility Extension"
+++
The `htmx-1-compat` extension allows you to almost seamlessly upgrade from htmx 1.x to htmx 2.
## Installing
The fastest way to install `htmx-1-compat` is to load it via a CDN. Remember to always include the core htmx library before the extension and enable the extension.
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-htmx-1-compat@2.0.0" integrity="sha384-lcvVWaNjF5zPPUeeWmC0OkJ2MLqoWLlkAabuGm+EuMSTfGo5WRyHrNaAp0cJr9Pg" crossorigin="anonymous"></script>
</head>
<body hx-ext="htmx-1-compat">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-htmx-1-compat/dist/htmx-1-compat.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `htmx-1-compat` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-htmx-1-compat`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `htmx-1-compat` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-htmx-1-compat
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-htmx-1-compat/dist/htmx-1-compat.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-htmx-1-compat` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-htmx-1-compat`;
\`\`\`
## What it covers
Htmx 2 introduced a few [breaking changes](https://v2-0v2-0.htmx.org/migration-guide-htmx-1/).
To make upgrading from htmx 1.x to htmx 2 easier, we're providing this extension that reverts most of those, so you're
able to benefit from the other changes without breaking your application.
### Obsolete attributes
- htmx 2 removed the deprecated [hx-ws](https://htmx.org/attributes/hx-ws/)
and [hx-sse](https://htmx.org/attributes/hx-sse/) attributes, that this extension restores.
- htmx 2 removed the deprecated `hx-on` attribute in favor of the
wildcard [`hx-on*` attribute](https://htmx.org/attributes/hx-on/), that this extension restores.
### Default Changes
- reverts [htmx.config.scrollBehavior](https://htmx.org/reference/#config) to 'smooth'.
- makes `DELETE` requests use a form-encoded body rather than URL parameters (htmx 2 uses URL parameters for `DELETE` as
default as per [the spec](https://www.rfc-editor.org/rfc/rfc9110.html#name-delete)).
- allows cross-domain requests by default (htmx 2 now forbids it by default).
## What it does not cover
- IE11 support was dropped in htmx 2, and this extension cannot revert that. If you need IE11 support, please stay with
htmx 1 that will continue being supported.
- htmx 2 introduced the breaking change that is the [swap method](https://v2-0v2-0.htmx.org/api/#swap) to the extensions
API. If you were only using core extensions, then you shouldn't need any additional work. If you were using custom or
community extensions, make sure that they were updated to work with htmx 2's API.
```
# extensions/idiomorph.md
```md
+++
title = "htmx Idiomorph Extension"
+++
[Idiomorph](https://github.com/bigskysoftware/idiomorph) is a DOM morphing algorithm created by the htmx creator. DOM
morphing is a process where an existing DOM tree is "morphed" into the shape of another in a way that resuses as much of
the existing DOM's nodes as possible. By preserving nodes when changing from one tree to another you can present a
much smoother transition between the two states.
You can use the idiomorph morphing algorithm as a [swapping](@attributes/hx-swap) strategy by including the idiomorph
extension.
## Installing
The fastest way to install `idiomorph` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/idiomorph@0.7.3" integrity="sha384-JcorokHTL/m+D6ZHe2+yFVQopVwZ+91GxAPDyEZ6/A/OEPGEx1+MeNSe2OGvoRS9" crossorigin="anonymous"></script>
<script src="https://unpkg.com/idiomorph@0.7.3/dist/idiomorph-ext.min.js" integrity="sha384-szktAZju9fwY15dZ6D2FKFN4eZoltuXiHStNDJWK9+FARrxJtquql828JzikODob" crossorigin="anonymous"></script>
</head>
<body hx-ext="morph">
\`\`\`
Unminified versions are also available at:
https://unpkg.com/idiomorph/dist/idiomorph.js
https://unpkg.com/idiomorph/dist/idiomorph-ext.js
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `idiomorph` is to simply copy it into your project. Download idiomorph and its htmx extension from `https://unpkg.com/idiomorph` and `https://unpkg.com/idiomorph/dist/idiomorph-ext.min.js`, add them to the appropriate directory in your project and include them where necessary with `<script>` tags.
For npm-style build systems, you can install `idiomorph` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install idiomorph
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/idiomorph/dist/idiomorph.js` (or `.min.js`) and `node_modules/idiomorph/dist/idiomorph-ext.js`. For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `idiomorph` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `idiomorph`;
\`\`\`
## Usage
Once you have referenced the idiomorph extension, you can register it with the name `morph` on the body and then being
using `morph`, `morph:outerHTML` or `morph:innerHTML` as swap strategies.
* `morph` & `morph:outerHTML` will morph the target element as well as it's children
* `morph:innerHTML` will morph only the inner children of an element, leaving the target untouched
\`\`\`html
<body hx-ext="morph">
<button hx-get="/example" hx-swap="morph">
Morph My Outer HTML
</button>
<button hx-get="/example" hx-swap="morph:outerHTML">
Morph My Outer HTML
</button>
<button hx-get="/example" hx-swap="morph:innerHTML">
Morph My Inner HTML
</button>
</body>
\`\`\`
```
# extensions/preload.md
```md
+++
title = "htmx Preload Extension"
+++
The `preload` extension allows you to load HTML fragments into your browser's cache before they are requested by the
user, so that additional pages appear to users to load nearly instantaneously. As a developer, you can customize its
behavior to fit your applications needs and use cases.
**IMPORTANT:** Preloading content judiciously can improve your web application's perceived performance, but preloading
too many resources can negatively impact your visitors' bandwidth and your server performance by initiating too many
unused requests. Use this extension carefully!
## Installing
The fastest way to install `preload` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-preload@2.1.0" integrity="sha384-fkzubQiTB69M7XTToqW6tplvxAOJkqPl5JmLAbumV2EacmuJb8xEP9KnJafk/rg8" crossorigin="anonymous"></script>
</head>
<body hx-ext="preload">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-preload/dist/preload.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `preload` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-preload`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `preload` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-preload
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-preload/dist/preload.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-preload` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-preload`;
\`\`\`
## Usage
Register the extension with htmx using the `hx-ext` attribute. Then, add a `preload` attribute to any hyperlinks
and `hx-get` elements you want to preload. By default, resources will be loaded as soon as the `mousedown` event begins,
giving your application a roughly 100-200ms head start on serving responses. See configuration below for other options.
\`\`\`html
<body hx-ext="preload">
<h1>What Works</h2>
<a href="/server/1" preload>WILL BE requested using a standard XMLHttpRequest() and default options (below)</a>
<button hx-get="/server/2" preload>WILL BE requested with additional htmx headers.</button>
<h1>What WILL NOT WORK</h1>
<a href="/server/3">WILL NOT be preloaded because it does not have an explicit "preload" attribute</a>
<a hx-post="/server/4" preload>WILL NOT be preloaded because it is an HX-POST transaction.</a>
</body>
\`\`\`
All preload requests include an additional `"HX-Preloaded": "true"` header.
### Inheriting Preload Settings
You can add the `preload` attribute to the top-level element that contains several `<a href="">` or `hx-get=""`
elements, and all of them will be preloaded. Be careful with this setting, because you can end up wasting bandwidth if
you preload many more resources than you need.
\`\`\`html
<body hx-ext="preload">
<ul preload>
<li><a href="/server/1">This will be preloaded because of the attribute in the node above.</a>
<li><a href="/server/2">This will also be preloaded for the same reason.</a>
<li><a href="/server/3">This will be preloaded, too. Lorem ipsum.</a>
</ul>
</body>
\`\`\`
### Preloading Forms
The extension can preload some form elements if the form includes `hx-get` attribute or uses `method="get"`. The `preload` attribute can be added to the form or to some of its selected elements. Currently these form elements can be preloaded:
- `<input type="radio>">` will be preloaded as if the radio button was clicked and form submitted
- `<input type="checkbox">` will be preloaded as if the checkbox was checked and form submitted
- `<input type="checkbox" checked>` will be preloaded as if the checkbox was unchecked and form submitted
- `<select>` will send multiple preload requests as if each unselected option was selected and form submitted
- `<input type="submit">` will be preloaded as if form was submitted
### Preloading of Linked Images
After an HTML page (or page fragment) is preloaded, this extension can also preload linked image resources. It will not
load or run linked Javascript or Cascading Stylesheet content, whether linked or embedded in the preloaded HTML. To
preload images as well, use the following syntax.
\`\`\`html
<div hx-ext="preload">
<a href="/my-next-page" preload="mouseover" preload-images="true">Next Page</a>
</div>
\`\`\`
### Configuration
Defaults for this extension are chosen to balance users' perceived performance with potential load on your servers from
unused requests. As a developer, you can modify two settings to customize this behavior to your specific use cases.
#### preload="mousedown" (DEFAULT)
The default behavior for this extension is to begin loading a resource when the user presses the mouse down. This is a
conservative setting that guarantees the user actually intends to use the linked resource. Because user click events
typically take 100-200ms to complete, this setting gives your server a significant headstart compared with a regular
click.
\`\`\`html
<a href="/server/1" preload="mousedown">This will be preloaded when the user begins to click.</a>
\`\`\`
#### preload="mouseover"
To preload links more aggressively, you can trigger the preload to happen when the user's mouse hovers over the link
instead. To prevent many resources from being loaded when the user scrolls or moves the mouse across a large list of
objects, a 100ms delay is built in to this action. If the user's mouse leaves the element *before* this timeout expires,
then the resource is not preloaded.
Typical users hover over links for several hundred milliseconds before they click, which gives your server even more
time to respond to the request than the `mousedown` option
above. [Test your own hover timing here.](http://instantclick.io/click-test). However, be careful when using this
option because it can increase server load by requesting resources unnecessarily.
\`\`\`html
<a href="/server/1" preload="mouseover">This will be preloaded when the user's mouse remains over it for more than
100ms.</a>
\`\`\`
#### preload="custom-event-name"
Preload can also listen to any custom event within the system, triggering resources to be preloaded (if they have not
already been cached by the browser). The extension itself generates an event called `preload:init` that can be used to
trigger preloads as soon as an object has been processed by htmx.
\`\`\`html
<body hx-ext="preload">
<button hx-get="/server" preload="preload:init" hx-target="idLoadMore">Load More</a>
<div id="idLoadMore">
Content for this DIV will be preloaded as soon as the page is ready.
Clicking the button above will swap it into the DOM.
</div>
</body>
\`\`\`
#### preload="always"
By default, the extension will preload each element once.
If you would like to keep always preloading the element, you can add `preload="always"` attribute.
This can be useful if `hx-target` is not the element itself.
This attribute can be combined with other configuration attributes e.g. `preload="always mouseover"`.
### About Touch Events
To accommodate touchscreen devices, an additional `ontouchstart` event handler is added whenever you specify
a `mouseover` or `mousedown` trigger. This extra trigger fires immediately (no waiting period) whenever the user touches
the screen, saving you 300ms of waiting time on Android, and 450ms on iOS.
### Limitations
* Links must be marked with a `preload` attribute, or have an ancestor node that has the `preload` attribute.
* Only `GET` transactions (including `<a href="">` and `hx-get=""`) can be preloaded. Following REST principles, `GET`
transactions are assumed to not make any significant changes to a resource. Transactions that can potentially make a
change (such as `POST`, `PUT`, and `DELETE`) will not be preloaded under any circumstances.
* When listening to `mouseover` events, preload waits for 100ms before downloading the linked resource. If the mouse
leaves the resource before this timeout expires, the resource is not preloaded.
* Preloaded responses will only be cached in the browser if the response headers allow it. For example, the response
header `Cache-Control: private, max-age=60` allows the browser to cache the response,
whereas `Cache-Control: no-cache` prevents it.
## Credits
The behavior for this plugin was inspired by the work done by [Alexandre Dieulot](https://github.com/dieulot)
on [InstantClick](http://instantclick.io/), which is released under the MIT license.
```
# extensions/response-targets.md
```md
+++
title = "htmx Response Targets Extension"
+++
This extension allows you to specify different target elements to be swapped when
different HTTP response codes are received.
It uses attribute names in a form of ``hx-target-[CODE]`` where `[CODE]` is a numeric
HTTP response code with the optional wildcard character at its end. You can also use
`hx-target-error`, which handles both 4xx and 5xx response codes.
The value of each attribute can be:
* A CSS query selector of the element to target.
* `this` which indicates that the element that the `hx-target` attribute is on is the target.
* `closest <CSS selector>` which will find the closest parent ancestor that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g `previous .error` will target the closest previous sibling with `error` class)
## Installing
The fastest way to install `response-targets` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-response-targets@2.0.2" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous"></script>
</head>
<body hx-ext="response-targets">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-response-targets/dist/response-targets.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `response-targets` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-response-targets`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `response-targets` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-response-targets
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-response-targets/dist/response-targets.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-response-targets` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-response-targets`;
\`\`\`
## Configure (optional)
* When `HX-Retarget` response header is received it disables any lookup that would be
performed by this extension but any responses with error status codes will be
swapped (normally they would not be, even with target set via header) and internal
error flag (`isError`) will be modified. You may change this and choose to ignore
`HX-Retarget` header when `hx-target-…` is in place by setting a configuration flag
`htmx.config.responseTargetPrefersRetargetHeader` to `false` (default is
`true`). Note that this extension only performs a simple check whether the header
is set and target exists. It is not extracting target's value from the header but
trusts it was set by HTMX core logic.
* Normally, any target which is already established by HTMX built-in functions or
extensions called before will be overwritten if a matching `hx-target-…` tag is
found. You may change it by using a configuration flag
`htmx.config.responseTargetPrefersExisting` to `true` (default is `false`). This is
kinky and risky option. It has a real-life applications similar to a skilled,
full-stack tardigrade eating parentheses when no one is watching.
* `isError` flag on the `detail` member of an event associated with swapping the
content with `hx-target-[CODE]` will be set to `false` when error response code is
received. This is different from the default behavior. You may change this by
setting a configuration flag `htmx.config.responseTargetUnsetsError` to `false`
(default is `true`).
* `isError` flag on the `detail` member of an event associated with swapping the
content with `hx-target-[CODE]` will be set to `false` when non-erroneous response
code is received. This is no different from the default behavior. You may change
this by setting a configuration flag `htmx.config.responseTargetSetsError` to
`true` (default is `false`). This setting will not affect the response code 200
since it is not handled by this extension.
## Usage
Here is an example that targets a `div` for normal (200) response but another `div`
for 404 (not found) response, and yet another for all 5xx response codes:
\`\`\`html
<div hx-ext="response-targets">
<div id="response-div"></div>
<button hx-post="/register"
hx-target="#response-div"
hx-target-5*="#serious-errors"
hx-target-404="#not-found">
Register!
</button>
<div id="serious-errors"></div>
<div id="not-found"></div>
</div>
\`\`\`
* The response from the `/register` URL will replace contents of the `div` with the
`id` `response-div` when response code is 200 (OK).
* The response from the `/register` URL will replace contents of the `div` with the `id`
`serious-errors` when response code begins with a digit 5 (server errors).
* The response from the `/register` URL will replace contents of the `div` with
the `id` `not-found` when response code is 404 (Not Found).
Sometimes you may not want to handle 5xx and 4xx errors separately, in which case you
can use `hx-target-error`:
\`\`\`html
<div hx-ext="response-targets">
<div id="response-div"></div>
<button hx-post="/register"
hx-target="#response-div"
hx-target-error="#any-errors">
Register!
</button>
<div id="any-errors"></div>
</div>
\`\`\`
2xx codes will be handled as in the previous example. However, when the response code is 5xx
or 4xx, the response from `/register` will replace the contents of the `div` with the `id`
`any-errors`.
## Wildcard resolution
When status response code does not match existing `hx-target-[CODE]` attribute name
then its numeric part expressed as a string is trimmed with last character being
replaced with the asterisk (`*`). This lookup process continues until the attribute
is found or there are no more digits.
For example, if a browser receives 404 error code, the following attribute names will
be looked up (in the given order):
* `hx-target-404`
* `hx-target-40*`
* `hx-target-4*`
* `hx-target-*`.
_If you are using tools that do not support asterisks in HTML attributes, you
may instead use the `x` character, e.g., `hx-target-4xx`._
## Notes
* `hx-target-…` is inherited and can be placed on a parent element.
* `hx-target-…` cannot be used to handle HTTP response code 200.
* `hx-target-…` will honor `HX-Retarget` by default and will prefer it over any
calculated target but it can be changed by disabling the
`htmx.config.responseTargetPrefersRetargetHeader` configuration option.
* To avoid surprises the `hx-ext` attribute used to enable this extension should be
placed on a parent element containing elements with `hx-target-…` and `hx-target`
attributes.
## See also
* [`hx-target`](https://htmx.org/attributes/hx-target), specifies the target element to be swapped
```
# extensions/sse.md
```md
+++
title = "htmx Server Sent Event (SSE) Extension"
+++
The `Server Sent Events` extension connects to
an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) directly
from HTML. It manages the connections to your web server, listens for server events, and then swaps their contents into
your htmx webpage in real-time.
SSE is a lightweight alternative to WebSockets that works over existing HTTP connections, so it is easy to use through
proxy servers and firewalls. Remember, SSE is a uni-directional service, so you cannot send any messages to an SSE
server once the connection has been established. If you need bi-directional communication, then you should consider
using [WebSockets](@/extensions/ws.md) instead.
This extension replaces the experimental `hx-sse` attribute built into previous versions of htmx. For help migrating
from older versions, see the migration guide at the bottom of this page.
Use the following attributes to configure how SSE connections behave:
* `sse-connect="<url>"` - The URL of the SSE server.
* `sse-swap="<message-name>"` - The name of the message to swap into the DOM.
* `hx-trigger="sse:<message-name>"` - SSE messages can also trigger HTTP callbacks using
the [`hx-trigger`](https://htmx.org/attributes/hx-trigger) attribute.
* `sse-close=<message-name>` - To close the EventStream gracefully when that message is received. This might be helpful
if you want to send information to a client that will eventually stop.
## Installing
The fastest way to install `sse` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-sse@2.2.2" integrity="sha384-Y4gc0CK6Kg+hmulDc6rZPJu0tqvk7EWlih0Oh+2OkAi1ZDlCbBDCQEE2uVk472Ky" crossorigin="anonymous"></script>
</head>
<body hx-ext="sse">
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-sse/dist/sse.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `sse` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-sse`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `sse` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-sse
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-sse/dist/sse.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-sse` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-sse`;
\`\`\`
## Usage
\`\`\`html
<div hx-ext="sse" sse-connect="/chatroom" sse-swap="message">
Contents of this box will be updated in real time
with every SSE message received from the chatroom.
</div>
\`\`\`
### Connecting to an SSE Server
To connect to an SSE server, use the `hx-ext="sse"` attribute to install the extension on that HTML element, then
add `sse-connect="<url>"` to the element to make the connection.
When designing your server application, remember that SSE works just like any HTTP request. Although you cannot send any
messages to the server after you have established a connection, you can send parameters to the server along with your
request. So, instead of making an SSE connection to your server at `https://my-server/chat-updates` you can also connect
to `https://my-server/chat-updates?friends=true&format=detailed`. This allows your server to customize its responses to
what your client needs.
### Receiving Named Events
SSE messages consist of an event name and a data packet. No other metadata is allowed in the message. Here is an
example:
\`\`\`txt
event: EventName
data: <div>Content to swap into your HTML page.</div>
\`\`\`
We'll use the `sse-swap` attribute to listen for this event and swap its contents into our webpage.
\`\`\`html
<div hx-ext="sse" sse-connect="/event-source" sse-swap="EventName"></div>
\`\`\`
Notice that the name `EventName` from the server's message must match the value in the `sse-swap` attribute. Your server
can use as many different event names as necessary, but be careful: browsers can only listen for events that have been
explicitly named. So, if your server sends an event named `ChatroomUpdate` but your browser is only listening for events
named `ChatUpdate` then the extra event will be discarded.
### Receiving Unnamed Events
SSE messages can also be sent without any event name. In this case, the browser uses the default name `message` in its
place. The same rules specified above still apply. If your server sends an unnamed message, then you must listen for it
by including `sse-swap="message"`. There is no option for using a catch-all name. Here's how this looks:
\`\`\`txt
data: <div>Content to swap into your HTML page.</div>
\`\`\`
\`\`\`html
<div hx-ext="sse" sse-connect="/event-source" sse-swap="message"></div>
\`\`\`
### Receiving Multiple Events
You can also listen to multiple events (named or unnamed) from a single EventSource. Listeners must be either 1) the
same element that contains the `hx-ext` and `sse-connect` attributes, or 2) child elements of the element containing
the `hx-ext` and `sse-connect` attributes.
\`\`\`html
Multiple events in the same element
<div hx-ext="sse" sse-connect="/server-url" sse-swap="event1,event2"></div>
Multiple events in different elements (from the same source).
<div hx-ext="sse" sse-connect="/server-url">
<div sse-swap="event1"></div>
<div sse-swap="event2"></div>
</div>
\`\`\`
### Trigger Server Callbacks
When a connection for server sent events has been established, child elements can listen for these events by using the
special [`hx-trigger`](https://htmx.org/attributes/hx-trigger) syntax `sse:<event_name>`. This, when combined with
an `hx-get` or similar will trigger the element to make a request.
Here is an example:
\`\`\`html
<div hx-ext="sse" sse-connect="/event_stream">
<div hx-get="/chatroom" hx-trigger="sse:chatter">
...
</div>
</div>
\`\`\`
This example establishes an SSE connection to the `event_stream` end point which then triggers
a `GET` to the `/chatroom` url whenever the `chatter` event is seen.
### Automatic Reconnection
If the SSE Event Stream is closed unexpectedly, browsers are supposed to attempt to reconnect automatically. However, in
rare situations this does not work and your browser can be left hanging. This extension adds its own reconnection
logic (using an [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff)) on top of the
browser's automatic reconnection, so that your SSE streams will always be as reliable as possible.
### Testing SSE Connections with the Demo Server
Htmx includes a demo SSE server written in Node.js that will help you to see SSE in action, and begin bootstrapping your
own SSE code. It is located in the /test/ws-sse folder of
the [`htmx-extensions`](https://github.com/bigskysoftware/htmx-extensions) repository. Look at /test/ws-sse/README.md
for instructions on running and using the test server.
### Migrating from Previous Versions
Previous versions of htmx used a built-in tag `hx-sse` to implement Server Sent Events. This code has been migrated into
an extension instead. Here are the steps you need to take to migrate to this version:
| Old Attribute | New Attribute | Comments |
|--------------------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `hx-sse=""` | `hx-ext="sse"` | Use the `hx-ext="sse"` attribute to install the SSE extension into any HTML element. |
| `hx-sse="connect:<url>"` | `sse-connect="<url>"` | Add a new attribute `sse-connect` to the tag that specifies the URL of the Event Stream. This attribute must be in the same tag as the `hx-ext` attribute. |
| `hx-sse="swap:<EventName>"` | `sse-swap="<EventName>"` | Add a new attribute `sse-swap` to any elements that will be swapped in via the SSE extension. This attribute must be placed **on** or **inside of** the tag containing the `hx-ext` attribute. |
| `hx-trigger="sse:<EventName>"` | NO CHANGE | any `hx-trigger` attributes do not need to change. The extension will identify these attributes and add listeners for any events prefixed with `sse:` |
### Listening to events dispatched by this extension
This extension dispatches several events. You can listen for these events like so:
\`\`\`javascript
document.body.addEventListener('htmx:sseBeforeMessage', function (e) {
// do something before the event data is swapped in
})
\`\`\`
Each event object has a `detail` field that contains details of the event.
#### `htmx:sseOpen`
This event is dispatched when an SSE connection has been successfully established.
##### Details
* `detail.elt` - The element on which the SSE connection was setup. This is the element which has the `sse-connect`
attribute.
* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) object.
#### `htmx:sseError`
This event is dispatched when an SSE connection could not be established.
##### Details
* `detail.error` - The error that occurred while creating
an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
#### `htmx:sseBeforeMessage`
This event is dispatched just before the SSE event data is swapped into the DOM. If you don't want to swap
call `preventDefault()` on the event. Additionally the `detail` field is
a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created
by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.
##### Details
* `detail.elt` - The swap target.
#### `htmx:sseMessage`
This event is dispatched after the SSE event data has been swapped into the DOM. The `detail` field is
a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created
by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.
#### `htmx:sseClose`
This event is dispatched in three different closing scenario. To control for the scenario the user can control for the
evt.detail.sseclose property.
\`\`\`javascript
document.body.addEventListener('htmx:sseClose', function (e) {
const reason = e.detail.type
switch (reason) {
case "nodeMissing":
// Parent node is missing and therefore connection was closed
...
case "nodeReplaced":
// Parent node replacement caused closing of connection
...
case "message":
// connection was closed due to reception of message sse-close
...
}
})
\`\`\`
##### Details
* `detail.elt` - The swap target.
### Additional SSE Resources
* [Wikipedia](https://en.wikipedia.org/wiki/Server-sent_events)
* [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
* [Can I Use?](https://caniuse.com/eventsource)
```
# extensions/ws.md
```md
+++
title = "htmx Web Socket extension"
+++
The Web Sockets extension enables easy, bi-directional communication
with [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
servers directly from HTML. This replaces the experimental `hx-ws` attribute built into previous versions of htmx. For
help migrating from older versions, see the [Migrating](#migrating-from-previous-versions) guide at the bottom of this
page.
Use the following attributes to configure how WebSockets behave:
* `ws-connect="<url>"` or `ws-connect="<prefix>:<url>"` - A URL to establish a `WebSocket` connection against.
* Prefixes `ws` or `wss` can optionally be specified. If not specified, HTMX defaults to adding the location's
scheme-type,
host and port to have browsers send cookies via websockets.
* `ws-send` - Sends a message to the nearest websocket based on the trigger value for the element (either the natural
event
or the event specified by [`hx-trigger`])
## Installing
The fastest way to install `ws` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-ws@2.0.2" integrity="sha384-vuKxTKv5TX/b3lLzDKP2U363sOAoRo5wSvzzc3LJsbaQRSBSS+3rKKHcOx5J8doU" crossorigin="anonymous"></script>
</head>
<body hx-ext="ws">
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-ws/dist/ws.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `ws` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-ws`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `ws` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-ws
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-ws/dist/ws.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-ws` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-ws`;
\`\`\`
## Usage
\`\`\`html
<div hx-ext="ws" ws-connect="/chatroom">
<div id="notifications"></div>
<div id="chat_room">
...
</div>
<form id="form" ws-send>
<input name="chat_message">
</form>
</div>
\`\`\`
### Configuration
WebSockets extension support two configuration options:
- `createWebSocket` - a factory function that can be used to create a custom WebSocket instances. Must be a function,
returning `WebSocket` object
- `wsBinaryType` - a string value, that defines
socket's [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType) property. Default value
is `blob`
### Receiving Messages from a WebSocket
The example above establishes a WebSocket to the `/chatroom` end point. Content that is sent down from the websocket
will
be parsed as HTML and swapped in by the `id` property, using the same logic
as [Out of Band Swaps](https://htmx.org/attributes/hx-swap-oob/).
As such, if you want to change the swapping method (e.g., append content at the end of an element or delegate swapping
to an extension),
you need to specify that in the message body, sent by the server.
\`\`\`html
<!-- will be interpreted as hx-swap-oob="true" by default -->
<form id="form">
...
</form>
<!-- will be appended to #notifications div -->
<div id="notifications" hx-swap-oob="beforeend">
New message received
</div>
<!-- will be swapped using an extension -->
<div id="chat_room" hx-swap-oob="morphdom">
....
</div>
\`\`\`
### Sending Messages to a WebSocket
In the example above, the form uses the `ws-send` attribute to indicate that when it is submitted, the form values
should be **serialized as JSON**
and send to the nearest enclosing `WebSocket`, in this case the `/chatroom` endpoint.
The serialized values will include a field, `HEADERS`, that includes the headers normally submitted with an htmx
request.
### Automatic Reconnection
If the WebSocket is closed unexpectedly, due to `Abnormal Closure`, `Service Restart` or `Try Again Later`, this
extension will attempt to reconnect until the connection is reestablished.
By default, the extension uses a
full-jitter [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) that chooses a randomized
retry delay that grows exponentially over time. You can use a different algorithm by writing it
into `htmx.config.wsReconnectDelay`. This function takes a single parameter, the number of retries, and returns the
time (in milliseconds) to wait before trying again.
\`\`\`javascript
// example reconnect delay that you shouldn't use because
// it's not as good as the algorithm that's already in place
htmx.config.wsReconnectDelay = function (retryCount) {
return retryCount * 1000 // return value in milliseconds
}
\`\`\`
The extension also implements a simple queuing mechanism that keeps messages in memory when the socket is not in `OPEN`
state and sends them once the connection is restored.
### Events
WebSockets extensions exposes a set of events that allow you to observe and customize its behavior.
#### Event - `htmx:wsConnecting` {#htmx:wsConnecting}
This event is triggered when a connection to a WebSocket endpoint is being attempted.
##### Details
* `detail.event.type` - the type of the event (`'connecting'`)
#### Event - `htmx:wsOpen` {#htmx:wsOpen}
This event is triggered when a connection to a WebSocket endpoint has been established.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.event` - the original event from the socket
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsClose` {#htmx:wsClose}
This event is triggered when a connection to a WebSocket endpoint has been closed normally.
You can check if the event was caused by an error by inspecting `detail.event` property.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.event` - the original event from the socket
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsError` {#htmx:wsError}
This event is triggered when `onerror` event on a socket is raised.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.error` - the error object
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsBeforeMessage` {#htmx:wsBeforeMessage}
This event is triggered when a message has just been received by a socket, similar to `htmx:beforeOnLoad`. This event
fires
before any processing occurs.
If the event is cancelled, no further processing will occur.
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.message` - raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsAfterMessage` {#htmx:wsAfterMessage}
This event is triggered when a message has been completely processed by htmx and all changes have been
settled, similar to `htmx:afterOnLoad`.
Cancelling this event has no effect.
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.message` - raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsConfigSend` {#htmx:wsConfigSend}
This event is triggered when preparing to send a message from `ws-send` element.
Similarly to [`htmx:configRequest`](https://htmx.org/events#htmx:configRequest), it allows you to modify the message
before sending.
If the event is cancelled, no further processing will occur and no messages will be sent.
##### Details
* `detail.parameters` - the parameters that will be submitted in the request
* `detail.unfilteredParameters` - the parameters that were found before filtering
by [`hx-params`](https://htmx.org/attributes/hx-params)
* `detail.headers` - the request headers. Will be attached to the body in `HEADERS` property, if not falsy
* `detail.errors` - validation errors. Will prevent sending and
trigger [`htmx:validation:halted`](https://htmx.org/events#htmx:validation:halted) event if not empty
* `detail.triggeringEvent` - the event that triggered sending
* `detail.messageBody` - raw message body that will be sent to the socket. Undefined, can be set to value of any type,
supported by WebSockets. If set, will override
default JSON serialization. Useful, if you want to use some other format, like XML or MessagePack
* `detail.elt` - the element that dispatched the sending (the one with `ws-send` attribute)
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsBeforeSend` {#htmx:wsBeforeSend}
This event is triggered just before sending a message. This includes messages from the queue.
Message can not be modified at this point.
If the event is cancelled, the message will be discarded from the queue and not sent.
##### Details
* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute)
* `detail.message` - the raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsAfterSend` {#htmx:wsAfterSend}
This event is triggered just after sending a message. This includes messages from the queue.
Cancelling the event has no effect.
##### Details
* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute)
* `detail.message` - the raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Socket wrapper
You may notice that all events expose `detail.socketWrapper` property. This wrapper holds the socket
object itself and the message queue. It also encapsulates reconnection algorithm. It exposes a few members:
- `send(message, fromElt)` - sends a message safely. If the socket is not open, the message will be persisted in the
queue
instead and sent when the socket is ready.
- `sendImmediately(message, fromElt)` - attempts to send a message regardless of socket state, bypassing the queue. May
fail
- `queue` - an array of messages, awaiting in the queue.
This wrapper can be used in your event handlers to monitor and manipulate the queue (e.g., you can reset the queue when
reconnecting), and to send additional messages (e.g., if you want to send data in batches).
The `fromElt` parameter is optional and, when specified, will trigger corresponding websocket events from
specified element, namely `htmx:wsBeforeSend` and `htmx:wsAfterSend` events when sending your messages.
### Testing with the Demo Server
Htmx includes a demo WebSockets server written in Node.js that will help you to see WebSockets in action, and begin
bootstrapping your own WebSockets code. It is located in the /test/ws-sse folder of
the [`htmx-extensions`](https://github.com/bigskysoftware/htmx-extensions) repository. Look at /test/ws-sse/README.md
for instructions on running and using the test server.
### Migrating from Previous Versions
Previous versions of htmx used a built-in tag `hx-ws` to implement WebSockets. This code has been migrated into an
extension instead. Here are the steps you need to take to migrate to this version:
| Old Attribute | New Attribute | Comments |
|-------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------|
| `hx-ws=""` | `hx-ext="ws"` | Use the `hx-ext="ws"` attribute to install the WebSockets extension into any HTML element. |
| `hx-ws="connect:<url>"` | `ws-connect="<url>"` | Add a new attribute `ws-connect` to the tag that defines the extension to specify the URL of the WebSockets server you're using. |
| `hx-ws="send"` | `ws-send=""` | Add a new attribute `ws-send` to mark any child forms that should send data to your WebSocket server |
```
# headers/hx-location.md
```md
+++
title = "HX-Location Response Header"
description = """\
Use the HX-Location response header in htmx to trigger a client-side redirection without reloading the whole page."""
+++
This response header can be used to trigger a client side redirection without reloading the whole page. Instead of changing the page's location it will act like following a [`hx-boost` link](@/attributes/hx-boost.md), creating a new history entry, issuing an ajax request to the value of the header and pushing the path into history.
A sample response would be:
\`\`\`html
HX-Location: /test
\`\`\`
Which would push the client to test as if the user had clicked on `<a href="/test" hx-boost="true">`
If you want to redirect to a specific target on the page rather than the default of document.body, you can pass more details along with the event, by using JSON for the value of the header:
\`\`\`html
HX-Location: {"path":"/test2", "target":"#testdiv"}
\`\`\`
Path is required and is url to load the response from. The rest of the data mirrors the [`ajax` api](@/api.md#ajax) context, which is:
* `source` - the source element of the request
* `event` - an event that "triggered" the request
* `handler` - a callback that will handle the response HTML
* `target` - the target to swap the response into
* `swap` - how the response will be swapped in relative to the target
* `values` - values to submit with the request
* `headers` - headers to submit with the request
* `select` - allows you to select the content you want swapped from a response
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-push-url.md
```md
+++
title = "HX-Push-Url Response Header"
description = """\
Use the HX-Push-Url response header in htmx to push a URL into the browser location history."""
+++
The `HX-Push-Url` header allows you to push a URL into the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
This creates a new history entry, allowing navigation with the browser’s back and forward buttons.
This is similar to the [`hx-push-url` attribute](@/attributes/hx-push-url.md).
If present, this header overrides any behavior defined with attributes.
The possible values for this header are:
1. A URL to be pushed into the location bar.
This may be relative or absolute, as per [`history.pushState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState).
2. `false`, which prevents the browser’s history from being updated.
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-push.md
```md
+++
title = "HX-Push Response Header (Deprecated)"
description = """\
The HX-Push response header in htmx is deprecated. Use HX-Push-Url instead."""
+++
The `HX-Push` header has been replaced by [`HX-Push-Url`](@/headers/hx-push-url.md)
```
# headers/hx-redirect.md
```md
+++
title = "HX-Redirect Response Header"
description = """\
Use the HX-Redirect response header in htmx to trigger a client-side redirection that will perform a full page \
reload."""
+++
This response header can be used to trigger a client side redirection to a new url that will do a full reload of the whole page. It uses the browser to redirect to the new location which can be useful when redirecting to non htmx endpoints that may contain different HTML `head` content or scripts. See [`HX-Location`](@/headers/hx-location.md) if you want more control over the redirect or want to use ajax requests instead of full browser reloads.
A sample response would be:
\`\`\`html
HX-Redirect: /test
\`\`\`
Which would push the client to test as if the user had entered this url manually or clicked on a non-boosted link `<a href="/test">`
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-replace-url.md
```md
+++
title = "HX-Replace-Url Response Header"
description = """\
Use the HX-Replace-Url response header in htmx to replace the current URL in the browser location history without \
creating a new history entry."""
+++
The `HX-Replace-Url` header allows you to replace the current URL in the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
This does not create a new history entry; in effect, it removes the previous current URL from the browser&rsquo;s history.
This is similar to the [`hx-replace-url` attribute](@/attributes/hx-replace-url.md).
If present, this header overrides any behavior defined with attributes.
The possible values for this header are:
1. A URL to replace the current URL in the location bar.
This may be relative or absolute, as per [`history.replaceState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState), but must have the same origin as the current URL.
2. `false`, which prevents the browser’s current URL from being updated.
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-trigger.md
```md
+++
title = "HX-Trigger Response Headers"
description = """\
Use the HX-Trigger family of response headers in htmx to trigger client-side actions from an htmx response."""
+++
These response headers can be used to trigger client side actions on the target element within a response to htmx. You
can trigger a single event or as many uniquely named events as you would like.
The headers are:
* `HX-Trigger` - trigger events as soon as the response is received.
* `HX-Trigger-After-Settle` - trigger events after the [settling step](@/docs.md#request-operations).
* `HX-Trigger-After-Swap` - trigger events after the [swap step](@/docs.md#request-operations).
To trigger a single event with no additional details you can simply send the event name in a header like so:
`HX-Trigger: myEvent`
This will trigger `myEvent` on the triggering element and will bubble up to the body. As an example you could
listen for this event like this:
\`\`\`javascript
document.body.addEventListener("myEvent", function(evt){
alert("myEvent was triggered!");
})
\`\`\`
... or like this, if you're trying to trigger some element without using JS code:
\`\`\`html
<!-- Since it bubbles up to the <body>, we must use the `from:body` modifier below -->
<div hx-trigger="myEvent from:body" hx-get="/example"></div>
\`\`\`
If you want to pass details along with the event, you can move to JSON for the value of the trigger:
`HX-Trigger: {"showMessage":"Here Is A Message"}`
To handle this event you would write the following code:
\`\`\`javascript
document.body.addEventListener("showMessage", function(evt){
alert(evt.detail.value);
})
\`\`\`
Note that the value of the message was put into the `detail.value` slot. If you wish to pass multiple pieces of data
you can use a nested JSON object on the right hand side of the JSON object:
`HX-Trigger: {"showMessage":{"level" : "info", "message" : "Here Is A Message"}}`
And handle this event like so:
\`\`\`javascript
document.body.addEventListener("showMessage", function(evt){
if(evt.detail.level === "info"){
alert(evt.detail.message);
}
})
\`\`\`
Each property of the JSON object on the right hand side will be copied onto the details object for the event.
### Targeting Other Elements
You can trigger events on other target elements by adding a `target` argument to the JSON object.
`HX-Trigger: {"showMessage":{"target" : "#otherElement"}}`
### Multiple Triggers
If you wish to invoke multiple events, you can simply add additional properties to the top level JSON
object:
`HX-Trigger: {"event1":"A message", "event2":"Another message"}`
You may also trigger multiple events with no additional details by sending event names separated by commas, like so:
`HX-Trigger: event1, event2`
Using events gives you a lot of flexibility to add functionality to normal htmx responses.
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# QUIRKS.md
```md
+++
title = "htmx quirks"
date = 2024-12-23
updated = 2024-12-23
[taxonomies]
author = ["Carson Gross"]
tag = ["posts"]
+++
This is a "quirks" page, based on [SQLite's "Quirks, Caveats, and Gotchas In SQLite" page](https://www.sqlite.org/quirks.html).
## Attribute Inheritance
Many attributes in htmx are [inherited](@/docs.md#inheritance): child elements can receive behavior from attributes located
on parent elements.
As an example, here are two htmx-powered buttons that inherit their [target](@/attributes/hx-target.md) from a parent
div:
\`\`\`html
<div hx-target="#output">
<button hx-post="/items/100/like">Like</button>
<button hx-delete="/items/100">Delete</button>
</div>
<output id="output"></output>
\`\`\`
This helps avoid repeating attributes, thus keeping code [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
On the other hand, as the attributes get further away elements, you lose [Locality of Behavior](@/essays/locality-of-behaviour.md)
and it becomes more difficult to understand what an element is doing.
It is also possible to inadvertently change the behavior of elements by adding attributes to parents.
Some people prefer to disable inheritance in htmx entirely, using the `htmx.config.disableInheritance`
[configuration variable](@/docs.md#config).
Here is a `meta` tag configuration that does so:
\`\`\`html
<meta name="htmx-config" content='{"disableInheritance":true}'>
\`\`\`
## The Default Swap Strategy is `innerHTML`
The [`hx-swap`](@/attributes/hx-swap.md) attribute allows you to control how a swap is performed. The default strategy is
`innerHTML`, that is, to place the response HTML content within the target element.
Many people prefer to use the `outerHTML` strategy as the default instead.
You can change this behavior using the `htmx.config.defaultSwapStyle`
[configuration variable](@/docs.md#config).
Here is a `meta` tag configuration that does so:
\`\`\`html
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
\`\`\`
## Targeting the `body` Always Performs an innerHTML Swap
For historical reasons, if you target the `body` element, htmx will
[always perform an `innerHTML` swap](https://github.com/bigskysoftware/htmx/blob/fb78106dc6ef20d3dfa7e54aca20408c4e4336fc/src/htmx.js#L1696).
This means you cannot change attributes on the `body` tag via an htmx request.
## By Default `4xx` & `5xx` Responses Do Not Swap
htmx has never swapped "error" status response codes (`400`s & `500`s) by default.
This behavior annoys some people, and some server frameworks, in particular, will return a `422 - Unprocessable Entity`
response code to indicate that a form was not filled out properly.
This can be very confusing when it is first encountered.
You can configure the response behavior of htmx via the [`htmx:beforeSwap`](@/docs.md#modifying_swapping_behavior_with_events)
event or [via the `htmx.config.responseHandling` config array](https://htmx.org/docs/#response-handling).
Here is the default configuration:
\`\`\`json
{
"responseHandling": [
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": false}]
}
\`\`\`
Note that `204 No Content` also is not swapped.
If you want to swap everything regardless of response code, you can use this configuration:
\`\`\`json
{
"responseHandling": [
{"code":"...", "swap": true}]
}
\`\`\`
If you want to specifically allow `422` responses to swap, you can use this configuration:
\`\`\`json
{
"responseHandling": [
{"code":"422", "swap": true},
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": false}]
}
\`\`\`
Here is a meta tag allowing all responses to swap:
\`\`\`html
<meta name="htmx-config" content='{"responseHandling": [{"code":"...", "swap": true}]}'>
\`\`\`
## `GET` Requests on Non-Form Elements Do Not Include Form Values by Default
If a non-form element makes a non-`GET` request (e.g. a `PUT` request) via htmx, the values of the enclosing form
of that element (if any) [will be included in the request](@/docs.md#parameters).
However, if the element issues a `GET`, the values of an enclosing form will
[not be included.](https://github.com/bigskysoftware/htmx/blob/fb78106dc6ef20d3dfa7e54aca20408c4e4336fc/src/htmx.js#L3525)
If you wish to include the values of the enclosing form when issuing an `GET` you can use the
[`hx-include`](@/attributes/hx-include.md) attribute like so:
\`\`\`html
<button hx-get="/search"
hx-include="closest form">
Search
</button>
\`\`\`
## History Can Be Tricky
htmx provides support for interacting with the browser's [history](@/docs.md#history). This can be very powerful, but it
can also be tricky, particularly if you are using 3rd party JavaScript libraries that modify the DOM.
There can also be [security concerns](@/docs.md#hx-history) when using htmx's history support.
Most of these issues can be solved by disabling any local history cache and simply issuing a server request when a
user navigates backwards in history, with the tradeoff that history navigation will be slower.
Here is a meta tag that disables history caching:
\`\`\`html
<meta name="htmx-config" content='{"historyCacheSize": 0}'>
\`\`\`
## Some People Don't Like `hx-boost`
[`hx-boost`](@/attributes/hx-boost.md) is an odd feature compared with most other aspects of htmx: it "magically" turns
all anchor tags and forms into AJAX requests.
This can speed the feel of these interactions up, and also allows the forms and anchors to continue working when
[JavaScript is disabled](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), however it comes
with some tradeoffs:
* The history issues mentioned above can show up
* Only the body of the web page will be updated, so any styles and scripts in the new page `head` tag will be discarded
* The global javascript scope is not refreshed, so it is possible to have strange interactions between pages. For example
a global `let` may start failing because a symbol is already defined.
Some members on the core htmx team feel that, due to these issues, as well as the fact that browsers have improved
quite a bit in page navigation, it is best to avoid `hx-boost` and
[just use unboosted links and forms](https://unplannedobsolescence.com/blog/less-htmx-is-more/).
There is no doubt that `hx-boost` is an odd-man out when compared to other htmx attributes and suffers from the dictum
that "If something magically works, then it can also magically break."
Despite this fact, I (Carson) still feel it is useful in many situations, and it is used on the <https://htmx.org>
website.
## The JavaScript API Is Not A Focus
htmx is a hypermedia-oriented front end library. This means that htmx enhances HTML via
[attributes](@/reference.md#attributes) in the HTML , rather than providing an elaborate
JavaScript API.
There _is_ a [JavaScript API](@/reference.md#api), but it is not a focus of the library and, in most cases,
should not be used heavily by htmx end users.
If you find yourself using it heavily, especially the [`htmx.ajax()`](@/api.md#ajax) method, it may be
worth asking yourself if there is a more htmx-ish approach to achieve what you are doing.
```
# api.md
```md
+++
title = "Javascript API"
description = """\
This documentation describes the JavaScript API for htmx, including methods and properties for configuring the \
behavior of htmx, working with CSS classes, AJAX requests, event handling, and DOM manipulation. The API provides \
helper functions primarily intended for extension development and event management."""
+++
While it is not a focus of the library, htmx does provide a small API of helper methods, intended mainly for [extension development](https://htmx.org/extensions) or for working with [events](@/events.md).
The [hyperscript](https://hyperscript.org) project is intended to provide more extensive scripting support
for htmx-based applications.
### Method - `htmx.addClass()` {#addClass}
This method adds a class to the given element.
##### Parameters
* `elt` - the element to add the class to
* `class` - the class to add
or
* `elt` - the element to add the class to
* `class` - the class to add
* `delay` - delay (in milliseconds ) before class is added
##### Example
\`\`\`js
// add the class 'myClass' to the element with the id 'demo'
htmx.addClass(htmx.find('#demo'), 'myClass');
// add the class 'myClass' to the element with the id 'demo' after 1 second
htmx.addClass(htmx.find('#demo'), 'myClass', 1000);
\`\`\`
### Method - `htmx.ajax()` {#ajax}
Issues an htmx-style AJAX request. This method returns a Promise, so a callback can be executed after the content has been inserted into the DOM.
##### Parameters
* `verb` - 'GET', 'POST', etc.
* `path` - the URL path to make the AJAX
* `element` - the element to target (defaults to the `body`)
or
* `verb` - 'GET', 'POST', etc.
* `path` - the URL path to make the AJAX
* `selector` - a selector for the target
or
* `verb` - 'GET', 'POST', etc.
* `path` - the URL path to make the AJAX
* `context` - a context object that contains any of the following
* `source` - the source element of the request, `hx-*` attrs which affect the request will be resolved against that element and its ancestors
* `event` - an event that "triggered" the request
* `handler` - a callback that will handle the response HTML
* `target` - the target to swap the response into
* `swap` - how the response will be swapped in relative to the target
* `values` - values to submit with the request
* `headers` - headers to submit with the request
* `select` - allows you to select the content you want swapped from a response
##### Example
\`\`\`js
// issue a GET to /example and put the response HTML into #myDiv
htmx.ajax('GET', '/example', '#myDiv')
// issue a GET to /example and replace #myDiv with the response
htmx.ajax('GET', '/example', {target:'#myDiv', swap:'outerHTML'})
// execute some code after the content has been inserted into the DOM
htmx.ajax('GET', '/example', '#myDiv').then(() => {
// this code will be executed after the 'htmx:afterOnLoad' event,
// and before the 'htmx:xhr:loadend' event
console.log('Content inserted successfully!');
});
\`\`\`
### Method - `htmx.closest()` {#closest}
Finds the closest matching element in the given elements parentage, inclusive of the element
##### Parameters
* `elt` - the element to find the selector from
* `selector` - the selector to find
##### Example
\`\`\`js
// find the closest enclosing div of the element with the id 'demo'
htmx.closest(htmx.find('#demo'), 'div');
\`\`\`
### Property - `htmx.config` {#config}
A property holding the configuration htmx uses at runtime.
Note that using a [meta tag](@/docs.md#config) is the preferred mechanism for setting these properties.
##### Properties
* `attributesToSettle:["class", "style", "width", "height"]` - array of strings: the attributes to settle during the settling phase
* `refreshOnHistoryMiss:false` - boolean: if set to `true` htmx will issue a full page refresh on history misses rather than use an AJAX request
* `defaultSettleDelay:20` - int: the default delay between completing the content swap and settling attributes
* `defaultSwapDelay:0` - int: the default delay between receiving a response from the server and doing the swap
* `defaultSwapStyle:'innerHTML'` - string: the default swap style to use if [`hx-swap`](@/attributes/hx-swap.md) is omitted
* `historyCacheSize:10` - int: the number of pages to keep in `localStorage` for history support
* `historyEnabled:true` - boolean: whether or not to use history
* `includeIndicatorStyles:true` - boolean: if true, htmx will inject a small amount of CSS into the page to make indicators invisible unless the `htmx-indicator` class is present
* `indicatorClass:'htmx-indicator'` - string: the class to place on indicators when a request is in flight
* `requestClass:'htmx-request'` - string: the class to place on triggering elements when a request is in flight
* `addedClass:'htmx-added'` - string: the class to temporarily place on elements that htmx has added to the DOM
* `settlingClass:'htmx-settling'` - string: the class to place on target elements when htmx is in the settling phase
* `swappingClass:'htmx-swapping'` - string: the class to place on target elements when htmx is in the swapping phase
* `allowEval:true` - boolean: allows the use of eval-like functionality in htmx, to enable `hx-vars`, trigger conditions & script tag evaluation. Can be set to `false` for CSP compatibility.
* `allowScriptTags:true` - boolean: allows script tags to be evaluated in new content
* `inlineScriptNonce:''` - string: the [nonce](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/nonce) to add to inline scripts
* `inlineStyleNonce:''` - string: the [nonce](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/nonce) to add to inline styles
* `withCredentials:false` - boolean: allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates
* `timeout:0` - int: the number of milliseconds a request can take before automatically being terminated
* `wsReconnectDelay:'full-jitter'` - string/function: the default implementation of `getWebSocketReconnectDelay` for reconnecting after unexpected connection loss by the event code `Abnormal Closure`, `Service Restart` or `Try Again Later`
* `wsBinaryType:'blob'` - string: the [the type of binary data](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) being received over the WebSocket connection
* `disableSelector:"[hx-disable], [data-hx-disable]"` - array of strings: htmx will not process elements with this attribute on it or a parent
* `disableInheritance:false` - boolean: If it is set to `true`, the inheritance of attributes is completely disabled and you can explicitly specify the inheritance with the [hx-inherit](@/attributes/hx-inherit.md) attribute.
* `scrollBehavior:'instant'` - string: the scroll behavior when using the [show](@/attributes/hx-swap.md#scrolling-scroll-show) modifier with `hx-swap`. The allowed values are `instant` (scrolling should happen instantly in a single jump), `smooth` (scrolling should animate smoothly) and `auto` (scroll behavior is determined by the computed value of [scroll-behavior](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior)).
* `defaultFocusScroll:false` - boolean: if the focused element should be scrolled into view, can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier
* `getCacheBusterParam:false` - boolean: if set to true htmx will append the target element to the `GET` request in the format `org.htmx.cache-buster=targetElementId`
* `globalViewTransitions:false` - boolean: if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content.
* `methodsThatUseUrlParams:["get", "delete"]` - array of strings: htmx will format requests with these methods by encoding their parameters in the URL, not the request body
* `selfRequestsOnly:true` - boolean: whether to only allow AJAX requests to the same domain as the current document
* `ignoreTitle:false` - boolean: if set to `true` htmx will not update the title of the document when a `title` tag is found in new content
* `scrollIntoViewOnBoost:true` - boolean: whether or not the target of a boosted element is scrolled into the viewport. If `hx-target` is omitted on a boosted element, the target defaults to `body`, causing the page to scroll to the top.
* `triggerSpecsCache:null` - object: the cache to store evaluated trigger specifications into, improving parsing performance at the cost of more memory usage. You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
* `htmx.config.responseHandling:[...]` - HtmxResponseHandlingConfig[]: the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error
* `htmx.config.allowNestedOobSwaps:true` - boolean: whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps).
##### Example
\`\`\`js
// update the history cache size to 30
htmx.config.historyCacheSize = 30;
\`\`\`
### Property - `htmx.createEventSource` {#createEventSource}
A property used to create new [Server Sent Event](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/sse/README.md) sources. This can be updated
to provide custom SSE setup.
##### Value
* `func(url)` - a function that takes a URL string and returns a new `EventSource`
##### Example
\`\`\`js
// override SSE event sources to not use credentials
htmx.createEventSource = function(url) {
return new EventSource(url, {withCredentials:false});
};
\`\`\`
### Property - `htmx.createWebSocket` {#createWebSocket}
A property used to create new [WebSocket](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ws/README.md). This can be updated
to provide custom WebSocket setup.
##### Value
* `func(url)` - a function that takes a URL string and returns a new `WebSocket`
##### Example
\`\`\`js
// override WebSocket to use a specific protocol
htmx.createWebSocket = function(url) {
return new WebSocket(url, ['wss']);
};
\`\`\`
### Method - `htmx.defineExtension()` {#defineExtension}
Defines a new htmx [extension](https://htmx.org/extensions).
##### Parameters
* `name` - the extension name
* `ext` - the extension definition
##### Example
\`\`\`js
// defines a silly extension that just logs the name of all events triggered
htmx.defineExtension("silly", {
onEvent : function(name, evt) {
console.log("Event " + name + " was triggered!")
}
});
\`\`\`
### Method - `htmx.find()` {#find}
Finds an element matching the selector
##### Parameters
* `selector` - the selector to match
or
* `elt` - the root element to find the matching element in, inclusive
* `selector` - the selector to match
##### Example
\`\`\`js
// find div with id my-div
var div = htmx.find("#my-div")
// find div with id another-div within that div
var anotherDiv = htmx.find(div, "#another-div")
\`\`\`
### Method - `htmx.findAll()` {#findAll}
Finds all elements matching the selector
##### Parameters
* `selector` - the selector to match
or
* `elt` - the root element to find the matching elements in, inclusive
* `selector` - the selector to match
##### Example
\`\`\`js
// find all divs
var allDivs = htmx.findAll("div")
// find all paragraphs within a given div
var allParagraphsInMyDiv = htmx.findAll(htmx.find("#my-div"), "p")
\`\`\`
### Method - `htmx.logAll()` {#logAll}
Log all htmx events, useful for debugging.
##### Example
\`\`\`js
htmx.logAll();
\`\`\`
### Method - `htmx.logNone()` {#logNone}
Log no htmx events, call this to turn off the debugger if you previously enabled it.
##### Example
\`\`\`js
htmx.logNone();
\`\`\`
### Property - `htmx.logger` {#logger}
The logger htmx uses to log with
##### Value
* `func(elt, eventName, detail)` - a function that takes an element, eventName and event detail and logs it
##### Example
\`\`\`js
htmx.logger = function(elt, event, data) {
if(console) {
console.log("INFO:", event, elt, data);
}
}
\`\`\`
### Method - `htmx.off()` {#off}
Removes an event listener from an element
##### Parameters
* `eventName` - the event name to remove the listener from
* `listener` - the listener to remove
or
* `target` - the element to remove the listener from
* `eventName` - the event name to remove the listener from
* `listener` - the listener to remove
##### Example
\`\`\`js
// remove this click listener from the body
htmx.off("click", myEventListener);
// remove this click listener from the given div
htmx.off("#my-div", "click", myEventListener)
\`\`\`
### Method - `htmx.on()` {#on}
Adds an event listener to an element
##### Parameters
* `eventName` - the event name to add the listener for
* `listener` - the listener to add
* `options` - an [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) object (or a [useCapture](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture) boolean) to add to the event listener (optional)
or
* `target` - the element to add the listener to
* `eventName` - the event name to add the listener for
* `listener` - the listener to add
* `options` - an [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) object (or a [useCapture](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture) boolean) to add to the event listener (optional)
##### Example
\`\`\`js
// add a click listener to the body
var myEventListener = htmx.on("click", function(evt){ console.log(evt); });
// add a click listener to the given div
var myEventListener = htmx.on("#my-div", "click", function(evt){ console.log(evt); });
// add a click listener to the given div that should only be invoked once
var myEventListener = htmx.on("#my-div", "click", function(evt){ console.log(evt); }, { once: true });
\`\`\`
### Method - `htmx.onLoad()` {#onLoad}
Adds a callback for the `htmx:load` event. This can be used to process new content, for example
initializing the content with a javascript library
##### Parameters
* `callback(elt)` - the callback to call on newly loaded content
##### Example
\`\`\`js
htmx.onLoad(function(elt){
MyLibrary.init(elt);
})
\`\`\`
### Method - `htmx.parseInterval()` {#parseInterval}
Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes.
Caution: Accepts an int followed by either `s` or `ms`. All other values use `parseFloat`
##### Parameters
* `str` - timing string
##### Example
\`\`\`js
// returns 3000
var milliseconds = htmx.parseInterval("3s");
// returns 3 - Caution
var milliseconds = htmx.parseInterval("3m");
\`\`\`
### Method - `htmx.process()` {#process}
Processes new content, enabling htmx behavior. This can be useful if you have content that is added to the DOM
outside of the normal htmx request cycle but still want htmx attributes to work.
##### Parameters
* `elt` - element to process
##### Example
\`\`\`js
document.body.innerHTML = "<div hx-get='/example'>Get it!</div>"
// process the newly added content
htmx.process(document.body);
\`\`\`
### Method - `htmx.remove()` {#remove}
Removes an element from the DOM
##### Parameters
* `elt` - element to remove
or
* `elt` - element to remove
* `delay` - delay (in milliseconds ) before element is removed
##### Example
\`\`\`js
// removes my-div from the DOM
htmx.remove(htmx.find("#my-div"));
// removes my-div from the DOM after a delay of 2 seconds
htmx.remove(htmx.find("#my-div"), 2000);
\`\`\`
### Method - `htmx.removeClass()` {#removeClass}
Removes a class from the given element
##### Parameters
* `elt` - element to remove the class from
* `class` - the class to remove
or
* `elt` - element to remove the class from
* `class` - the class to remove
* `delay` - delay (in milliseconds ) before class is removed
##### Example
\`\`\`js
// removes .myClass from my-div
htmx.removeClass(htmx.find("#my-div"), "myClass");
// removes .myClass from my-div after 6 seconds
htmx.removeClass(htmx.find("#my-div"), "myClass", 6000);
\`\`\`
### Method - `htmx.removeExtension()` {#removeExtension}
Removes the given extension from htmx
##### Parameters
* `name` - the name of the extension to remove
##### Example
\`\`\`js
htmx.removeExtension("my-extension");
\`\`\`
### Method - `htmx.swap()` {#swap}
Performs swapping (and settling) of HTML content
##### Parameters
* `target` - the HTML element or string selector of swap target
* `content` - string representation of content to be swapped
* `swapSpec` - swapping specification, representing parameters from `hx-swap`
* `swapStyle` (required) - swapping style (`innerHTML`, `outerHTML`, `beforebegin` etc)
* `swapDelay`, `settleDelay` (number) - delays before swapping and settling respectively
* `transition` (bool) - whether to use HTML transitions for swap
* `ignoreTitle` (bool) - disables page title updates
* `head` (string) - specifies `head` tag handling strategy (`merge` or `append`). Leave empty to disable head handling
* `scroll`, `scrollTarget`, `show`, `showTarget`, `focusScroll` - specifies scroll handling after swap
* `swapOptions` - additional *optional* parameters for swapping
* `select` - selector for the content to be swapped (equivalent of `hx-select`)
* `selectOOB` - selector for the content to be swapped out-of-band (equivalent of `hx-select-oob`)
* `eventInfo` - an object to be attached to `htmx:afterSwap` and `htmx:afterSettle` elements
* `anchor` - an anchor element that triggered scroll, will be scrolled into view on settle. Provides simple alternative to full scroll handling
* `contextElement` - DOM element that serves as context to swapping operation. Currently used to find extensions enabled for specific element
* `afterSwapCallback`, `afterSettleCallback` - callback functions called after swap and settle respectively. Take no arguments
##### Example
\`\`\`js
// swap #output element inner HTML with div element with "Swapped!" text
htmx.swap("#output", "<div>Swapped!</div>", {swapStyle: 'innerHTML'});
\`\`\`
### Method - `htmx.takeClass()` {#takeClass}
Takes the given class from its siblings, so that among its siblings, only the given element will have the class.
##### Parameters
* `elt` - the element that will take the class
* `class` - the class to take
##### Example
\`\`\`js
// takes the selected class from tab2's siblings
htmx.takeClass(htmx.find("#tab2"), "selected");
\`\`\`
### Method - `htmx.toggleClass()` {#toggleClass}
Toggles the given class on an element
##### Parameters
* `elt` - the element to toggle the class on
* `class` - the class to toggle
##### Example
\`\`\`js
// toggles the selected class on tab2
htmx.toggleClass(htmx.find("#tab2"), "selected");
\`\`\`
### Method - `htmx.trigger()` {#trigger}
Triggers a given event on an element
##### Parameters
* `elt` - the element to trigger the event on
* `name` - the name of the event to trigger
* `detail` - details for the event
##### Example
\`\`\`js
// triggers the myEvent event on #tab2 with the answer 42
htmx.trigger("#tab2", "myEvent", {answer:42});
\`\`\`
### Method - `htmx.values()` {#values}
Returns the input values that would resolve for a given element via the htmx value resolution mechanism
##### Parameters
* `elt` - the element to resolve values on
* `request type` - the request type (e.g. `get` or `post`) non-GET's will include the enclosing form of the element.
Defaults to `post`
##### Example
\`\`\`js
// gets the values associated with this form
var values = htmx.values(htmx.find("#myForm"));
\`\`\`
```
# attributes/hx-boost.md
```md
+++
title = "hx-boost"
description = """\
The hx-boost attribute in htmx enables progressive enhancement by converting standard HTML anchors and forms into \
AJAX requests, maintaining graceful fallback for users without JavaScript while providing modern dynamic page \
updates for those with JavaScript enabled."""
+++
The `hx-boost` attribute allows you to "boost" normal anchors and form tags to use AJAX instead. This
has the [nice fallback](https://en.wikipedia.org/wiki/Progressive_enhancement) that, if the user does not
have javascript enabled, the site will continue to work.
For anchor tags, clicking on the anchor will issue a `GET` request to the url specified in the `href` and
will push the url so that a history entry is created. The target is the `<body>` tag, and the `innerHTML`
swap strategy is used by default. All of these can be modified by using the appropriate attributes, except
the `click` trigger.
For forms the request will be converted into a `GET` or `POST`, based on the method in the `method` attribute
and will be triggered by a `submit`. Again, the target will be the `body` of the page, and the `innerHTML`
swap will be used. The url will _not_ be pushed, however, and no history entry will be created. (You can use the
[hx-push-url](@/attributes/hx-push-url.md) attribute if you want the url to be pushed.)
Here is an example of some boosted links:
\`\`\`html
<div hx-boost="true">
<a href="/page1">Go To Page 1</a>
<a href="/page2">Go To Page 2</a>
</div>
\`\`\`
These links will issue an ajax `GET` request to the respective URLs and replace the body's inner content with it.
Here is an example of a boosted form:
\`\`\`html
<form hx-boost="true" action="/example" method="post">
<input name="email" type="email" placeholder="Enter email...">
<button>Submit</button>
</form>
\`\`\`
This form will issue an ajax `POST` to the given URL and replace the body's inner content with it.
## Notes
* `hx-boost` is inherited and can be placed on a parent element
* Only links that are to the same domain and that are not local anchors will be boosted
* All requests are done via AJAX, so keep that in mind when doing things like redirects
* To find out if the request results from a boosted anchor or form, look for [`HX-Boosted`](@/reference.md#request_headers) in the request header
* Selectively disable boost on child elements with `hx-boost="false"`
* Disable the replacement of elements via boost, and their children, with [`hx-preserve="true"`](@/attributes/hx-preserve.md)
```
# attributes/hx-confirm.md
```md
+++
title = "hx-confirm"
description = """\
The hx-confirm attribute in htmx provides a way to add confirmation dialogs before executing requests, allowing \
you to protect users from accidental destructive actions. This documentation explains how to implement confirmation \
prompts and customize their behavior through event handling."""
+++
The `hx-confirm` attribute allows you to confirm an action before issuing a request. This can be useful
in cases where the action is destructive and you want to ensure that the user really wants to do it.
Here is an example:
\`\`\`html
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
\`\`\`
## Event details
The event triggered by `hx-confirm` contains additional properties in its `detail`:
* triggeringEvent: the event that triggered the original request
* issueRequest(skipConfirmation=false): a callback which can be used to confirm the AJAX request
* question: the value of the `hx-confirm` attribute on the HTML element
## Notes
* `hx-confirm` is inherited and can be placed on a parent element
* `hx-confirm` uses the browser's `window.confirm` by default. You can customize this behavior as shown [in this example](@/examples/confirm.md).
* a boolean `skipConfirmation` can be passed to the `issueRequest` callback; if true (defaults to false), the `window.confirm` will not be called and the AJAX request is issued directly
```
# attributes/hx-delete.md
```md
+++
title = "hx-delete"
description = """\
The hx-delete attribute in htmx will cause an element to issue a DELETE request to the specified URL and swap the \
returned HTML into the DOM using a swap strategy."""
+++
The `hx-delete` attribute will cause an element to issue a `DELETE` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-delete="/account" hx-target="body">
Delete Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `DELETE` to `/account` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-delete` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
* To remove the element following a successful `DELETE`, return a `200` status code with an empty body; if the server responds with a `204`, no swap takes place, documented here: [Requests & Responses](@/docs.md#requests)
```
# attributes/hx-disable.md
```md
+++
title = "hx-disable"
description = "The hx-disable attribute in htmx will disable htmx processing for a given element and all its children."
+++
The `hx-disable` attribute will disable htmx processing for a given element and all its children. This can be
useful as a backup for HTML escaping, when you include user generated content in your site, and you want to
prevent malicious scripting attacks.
The value of the tag is ignored, and it cannot be reversed by any content beneath it.
## Notes
* `hx-disable` is inherited
```
# attributes/hx-disabled-elt.md
```md
+++
title = "hx-disabled-elt"
description = """\
The hx-disabled-elt attribute in htmx allows you to specify elements that will have the `disabled` attribute added \
to them for the duration of the request."""
+++
The `hx-disabled-elt` attribute allows you to specify elements that will have the `disabled` attribute
added to them for the duration of the request. The value of this attribute can be:
* A CSS query selector of the element to disable.
* `this` to disable the element itself
* `closest <CSS selector>` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector
(e.g. `closest fieldset` will disable the closest to the element `fieldset`).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector
* `next` which resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling)
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector
(e.g. `next button` will disable the closest following sibling `button` element)
* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous input` will disable the closest previous sibling `input` element)
Here is an example with a button that will disable itself during a request:
\`\`\`html
<button hx-post="/example" hx-disabled-elt="this">
Post It!
</button>
\`\`\`
When a request is in flight, this will cause the button to be marked with [the `disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled),
which will prevent further clicks from occurring.
The `hx-disabled-elt` attribute also supports specifying multiple CSS selectors separated by commas to disable multiple elements during the request. Here is an example that disables buttons and text input fields of a particular form during the request:
\`\`\`html
<form hx-post="/example" hx-disabled-elt="find input[type='text'], find button">
<input type="text" placeholder="Type here...">
<button type="submit">Send</button>
</form>
\`\`\`
## Notes
* `hx-disabled-elt` is inherited and can be placed on a parent element
[hx-trigger]: https://htmx.org/attributes/hx-trigger/
```
# attributes/hx-disinherit.md
```md
+++
title = "hx-disinherit"
description = """\
The hx-disinherit attribute in htmx lets you control how child elements inherit attributes from their parents. This \
documentation explains how to selectively disable inheritance of specific htmx attributes or all attributes, \
allowing for more granular control over your web application's behavior."""
+++
The default behavior for htmx is to "inherit" many attributes automatically: that is, an attribute such as
[hx-target](@/attributes/hx-target.md) may be placed on a parent element, and all child elements will inherit
that target.
The `hx-disinherit` attribute allows you to control this automatic attribute inheritance. An example scenario is to
allow you to place an `hx-boost` on the `body` element of a page, but overriding that behavior in a specific part
of the page to allow for more specific behaviors.
htmx evaluates attribute inheritance as follows:
* when `hx-disinherit` is set on a parent node
* `hx-disinherit="*"` all attribute inheritance for this element will be disabled
* `hx-disinherit="hx-select hx-get hx-target"` disable inheritance for only one or multiple specified attributes
\`\`\`html
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-disinherit="*">
<a href="/page1">Go To Page 1</a> <!-- boosted with the attribute settings above -->
<a href="/page2" hx-boost="unset">Go To Page 1</a> <!-- not boosted -->
<button hx-get="/test" hx-target="this"></button> <!-- hx-select is not inherited -->
</div>
\`\`\`
\`\`\`html
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-disinherit="hx-target">
<!-- hx-select is automatically set to parent's value; hx-target is not inherited -->
<button hx-get="/test"></button>
</div>
\`\`\`
\`\`\`html
<div hx-select="#content">
<div hx-boost="true" hx-target="#content" hx-disinherit="hx-select">
<!-- hx-target is automatically inherited from parent's value -->
<!-- hx-select is not inherited, because the direct parent does
disables inheritance, despite not specifying hx-select itself -->
<button hx-get="/test"></button>
</div>
</div>
\`\`\`
## Notes
* Read more about [Attribute Inheritance](@/docs.md#inheritance)
```
# attributes/hx-encoding.md
```md
+++
title = "hx-encoding"
description = """\
The hx-encoding attribute in htmx allows you to switch the request encoding from the usual \
`application/x-www-form-urlencoded` encoding to `multipart/form-data`, usually to support file uploads in an AJAX \
request."""
+++
The `hx-encoding` attribute allows you to switch the request encoding from the usual `application/x-www-form-urlencoded`
encoding to `multipart/form-data`, usually to support file uploads in an ajax request.
The value of this attribute should be `multipart/form-data`.
The `hx-encoding` tag may be placed on parent elements.
## Notes
* `hx-encoding` is inherited and can be placed on a parent element
```
# attributes/hx-ext.md
```md
+++
title = "hx-ext"
description = """\
The hx-ext attribute in htmx enables one or more htmx extensions for an element and all its children. You can also \
use this attribute to ignore an extension that is enabled by a parent element."""
+++
The `hx-ext` attribute enables an htmx [extension](https://htmx.org/extensions) for an element and all its children.
The value can be a single extension name or a comma-separated list of extensions to apply.
The `hx-ext` tag may be placed on parent elements if you want a plugin to apply to an entire swath of the DOM,
and on the `body` tag for it to apply to all htmx requests.
## Notes
* `hx-ext` is both inherited and merged with parent elements, so you can specify extensions on any element in the DOM
hierarchy and it will apply to all child elements.
* You can ignore an extension that is defined by a parent node using `hx-ext="ignore:extensionName"`
\`\`\`html
<div hx-ext="example">
"Example" extension is used in this part of the tree...
<div hx-ext="ignore:example">
... but it will not be used in this part.
</div>
</div>
\`\`\`
\`\`\`html
<body hx-ext="preload,morph">
"preload" and "morph" extensions are used in this part of the tree...
</body>
\`\`\`
```
# attributes/hx-get.md
```md
+++
title = "hx-get"
description = """\
The hx-get attribute in htmx will cause an element to issue a GET request to the specified URL and swap the returned \
HTML into the DOM using a swap strategy."""
+++
The `hx-get` attribute will cause an element to issue a `GET` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-get="/example">Get Some HTML</button>
\`\`\`
This example will cause the `button` to issue a `GET` to `/example` and swap the returned HTML into
the `innerHTML` of the `button`.
### Notes
* `hx-get` is not inherited
* By default `hx-get` usually does not include any parameters. You can use the [hx-params](@/attributes/hx-params.md)
attribute to change this
* NB: If the element with the `hx-get` attribute also has a value, this will be included as a parameter unless explicitly removed
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
* An empty `hx-get:""` will make a get request to the current url and will swap the current HTML page
```
# attributes/hx-headers.md
```md
+++
title = "hx-headers"
description = """\
The hx-headers attribute in htmx allows you to add to the headers that will be submitted with an AJAX request."""
+++
The `hx-headers` attribute allows you to add to the headers that will be submitted with an AJAX request.
By default, the value of this attribute is a list of name-expression values in [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html)
format.
If you wish for `hx-headers` to *evaluate* the values given, you can prefix the values with `javascript:` or `js:`.
\`\`\`html
<div hx-get="/example" hx-headers='{"myHeader": "My Value"}'>Get Some HTML, Including A Custom Header in the Request</div>
<div hx-get="/example" hx-headers='js:{myVal: calculateValue()}'>Get Some HTML, Including a Dynamic Custom Header from Javascript in the Request</div>
\`\`\`
## Security Considerations
* By default, the value of `hx-headers` must be valid [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON).
It is **not** dynamically computed. If you use the `javascript:` prefix, be aware that you are introducing
security considerations, especially when dealing with user input such as query strings or user-generated content,
which could introduce a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability.
* Whilst far from being a foolproof solution to [Cross-Site Request Forgery](https://owasp.org/www-community/attacks/csrf), the `hx-headers` attribute can support backend services to provide [CSRF prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html). For more information see the [CSRF Prevention](https://htmx.org/docs/#csrf-prevention) section.
## Notes
* `hx-headers` is inherited and can be placed on a parent element.
* A child declaration of a header overrides a parent declaration.
```
# attributes/hx-history-elt.md
```md
+++
title = "hx-history-elt"
description = """\
The hx-history-elt attribute in htmx allows you to specify the element that will be used to snapshot and restore \
page state during navigation. In most cases we do not recommend using this element."""
+++
The `hx-history-elt` attribute allows you to specify the element that will be used to snapshot and
restore page state during navigation. By default, the `body` tag is used. This is typically
good enough for most setups, but you may want to narrow it down to a child element. Just make
sure that the element is always visible in your application, or htmx will not be able to restore
history navigation properly.
Here is an example:
\`\`\`html
<html>
<body>
<div id="content" hx-history-elt>
...
</div>
</body>
</html>
\`\`\`
## Notes
* `hx-history-elt` is not inherited
* In most cases we don't recommend narrowing the history snapshot
```
# attributes/hx-history.md
```md
+++
title = "hx-history"
description = """\
The hx-history attribute in htmx allows you to prevent sensitive page data from being stored in the browser's \
localStorage cache during history navigation, ensuring that the page state is retrieved from the server instead when \
navigating through history."""
+++
Set the `hx-history` attribute to `false` on any element in the current document, or any html fragment loaded into the current document by htmx, to prevent sensitive data being saved to the localStorage cache when htmx takes a snapshot of the page state.
History navigation will work as expected, but on restoration the URL will be requested from the server instead of the history cache.
Here is an example:
\`\`\`html
<html>
<body>
<div hx-history="false">
...
</div>
</body>
</html>
\`\`\`
## Notes
* `hx-history="false"` can be present *anywhere* in the document to embargo the current page state from the history cache (i.e. even outside the element specified for the history snapshot [hx-history-elt](@/attributes/hx-history-elt.md)).
```
# attributes/hx-include.md
```md
+++
title = "hx-include"
description = "The hx-include attribute in htmx allows you to include additional element values in an AJAX request."
+++
The `hx-include` attribute allows you to include additional element values in an AJAX request. The value of this
attribute can be:
* A CSS query selector of the elements to include.
* `this` which will include the descendants of the element.
* `closest <CSS selector>` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous .error` will target the closest previous sibling with `error` class)
Here is an example that includes a separate input value:
\`\`\`html
<div>
<button hx-post="/register" hx-include="[name='email']">
Register!
</button>
Enter email: <input name="email" type="email"/>
</div>
\`\`\`
This is a little contrived as you would typically enclose both of these elements in a `form` and submit
the value automatically, but it demonstrates the concept.
Note that if you include a non-input element, all input elements enclosed in that element will be included.
## Notes
* `hx-include` is inherited and can be placed on a parent element
* While `hx-include` is inherited, it is evaluated from the element triggering the request. It is easy to get confused
when working with the extended selectors such as `find` and `closest`.
\`\`\`html
<div hx-include="find input">
<button hx-post="/register">
Register!
</button>
Enter email: <input name="email" type="email"/>
</div>
\`\`\`
In the above example, when clicking on the button, the `find input` selector is resolved from the button itself, which
does not return any element here, since the button doesn't have any `input` child, thus in this case, raises an error.
* A standard CSS selector resolves
to [document.querySelectorAll](https://developer.mozilla.org/docs/Web/API/Document/querySelectorAll) and will include
multiple elements, while the extended selectors such as `find` or `next` only return a single element at most to
include
* `hx-include` will ignore disabled inputs
```
# attributes/hx-indicator.md
```md
+++
title = "hx-indicator"
description = """\
The hx-indicator attribute in htmx allows you to specify the element that will have the `htmx-request` class added \
to it for the duration of the request. This can be used to show spinners or progress indicators while the request is \
in flight."""
+++
The `hx-indicator` attribute allows you to specify the element that will have the `htmx-request` class
added to it for the duration of the request. This can be used to show spinners or progress indicators
while the request is in flight.
The value of this attribute is a CSS query selector of the element or elements to apply the class to,
or the keyword [`closest`](https://developer.mozilla.org/docs/Web/API/Element/closest), followed by a CSS selector,
which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. `closest tr`);
Here is an example with a spinner adjacent to the button:
\`\`\`html
<div>
<button hx-post="/example" hx-indicator="#spinner">
Post It!
</button>
<img id="spinner" class="htmx-indicator" src="/img/bars.svg"/>
</div>
\`\`\`
When a request is in flight, this will cause the `htmx-request` class to be added to the `#spinner`
image. The image also has the `htmx-indicator` class on it, which defines an opacity transition
that will show the spinner:
\`\`\`css
.htmx-indicator{
opacity:0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator{
opacity:1;
}
.htmx-request.htmx-indicator{
opacity:1;
}
\`\`\`
If you would prefer a different effect for showing the spinner you could define and use your own indicator
CSS. Here is an example that uses `display` rather than opacity (Note that we use `my-indicator` instead of `htmx-indicator`):
\`\`\`css
.my-indicator{
display:none;
}
.htmx-request .my-indicator{
display:inline;
}
.htmx-request.my-indicator{
display:inline;
}
\`\`\`
Note that the target of the `hx-indicator` selector need not be the exact element that you
want to show: it can be any element in the parent hierarchy of the indicator.
Finally, note that the `htmx-request` class by default is added to the element causing
the request, so you can place an indicator inside of that element and not need to explicitly
call it out with the `hx-indicator` attribute:
\`\`\`html
<button hx-post="/example">
Post It!
<img class="htmx-indicator" src="/img/bars.svg"/>
</button>
\`\`\`
## Demo
This simulates what a spinner might look like in that situation:
<button class="btn" classes="toggle htmx-request:3s">
Post It!
<img class="htmx-indicator" src="/img/bars.svg"/>
</button>
## Notes
* `hx-indicator` is inherited and can be placed on a parent element
* In the absence of an explicit indicator, the `htmx-request` class will be added to the element triggering the
request
* If you want to use your own CSS but still use `htmx-indicator` as class name, then you need to disable `includeIndicatorStyles`. See [Configuring htmx](@/docs.md#config). The easiest way is to add this to the `<head>` of your HTML:
\`\`\`html
<meta name="htmx-config" content='{"includeIndicatorStyles": false}'>
\`\`\`
```
# attributes/hx-inherit.md
```md
+++
title = "hx-inherit"
description = """\
The hx-inherit attribute in htmx allows you to explicitly control attribute inheritance behavior between parent and \
child elements, providing fine-grained control over which htmx attributes are inherited when the default inheritance \
system is disabled through configuration."""
+++
The default behavior for htmx is to "inherit" many attributes automatically: that is, an attribute such as
[hx-target](@/attributes/hx-target.md) may be placed on a parent element, and all child elements will inherit
that target. Some people do not like this feature and instead prefer to explicitly specify inheritance for attributes.
To support this mode of development, htmx offers the `htmx.config.disableInheritance` setting, which can be set to
`true` to prevent inheritance from being the default behavior for any of the htmx attributes.
The `hx-inherit` attribute allows you to control the inheritance of attributes manually.
htmx evaluates attribute inheritance as follows:
* when `hx-inherit` is set on a parent node
* `inherit="*"` all attribute inheritance for this element will be enabled
* `hx-inherit="hx-select hx-get hx-target"` enable inheritance for only one or multiple specified attributes
Here is an example of a div that shares an `hx-target` attribute for a set of anchor tags when `htmx.config.disableInheritance`
is set to false:
\`\`\`html
<div hx-target="#tab-container" hx-inherit="hx-target">
<a hx-boost="true" href="/tab1">Tab 1</a>
<a hx-boost="true" href="/tab2">Tab 2</a>
<a hx-boost="true" href="/tab3">Tab 3</a>
</div>
\`\`\`
## Notes
* Read more about [Attribute Inheritance](@/docs.md#inheritance)
```
# attributes/hx-on.md
```md
+++
title = "hx-on"
description = """\
The hx-on attributes in htmx allow you to write inline JavaScript event handlers directly on HTML elements, \
supporting both standard DOM events and htmx-specific events with improved locality of behavior."""
+++
The `hx-on*` attributes allow you to embed scripts inline to respond to events directly on an element; similar to the
[`onevent` properties](https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers#using_onevent_properties) found in HTML, such as `onClick`.
The `hx-on*` attributes improve upon `onevent` by enabling the handling of any arbitrary JavaScript event,
for enhanced [Locality of Behaviour (LoB)](/essays/locality-of-behaviour/) even when dealing with non-standard DOM events. For example, these
attributes allow you to handle [htmx events](/reference#events).
With `hx-on` attributes, you specify the event name as part of the attribute name, after a colon. So, for example, if
you want to respond to a `click` event, you would use the attribute `hx-on:click`:
\`\`\`html
<div hx-on:click="alert('Clicked!')">Click</div>
\`\`\`
Note that this syntax can be used to capture all htmx events, as well as most other custom events, in addition to the
standard DOM events.
One gotcha to note is that DOM attributes do not preserve case. This means, unfortunately, an attribute like
`hx-on:htmx:beforeRequest` **will not work**, because the DOM lowercases the attribute names. Fortunately, htmx supports
both camel case event names and also [kebab-case event names](@/docs.md#events), so you can use `hx-on:htmx:before-request` instead.
In order to make writing htmx-based event handlers a little easier, you can use the shorthand double-colon `hx-on::` for htmx
events, and omit the "htmx" part:
\`\`\`html
<!-- These two are equivalent -->
<button hx-get="/info" hx-on:htmx:before-request="alert('Making a request!')">
Get Info!
</button>
<button hx-get="/info" hx-on::before-request="alert('Making a request!')">
Get Info!
</button>
\`\`\`
If you wish to handle multiple different events, you can simply add multiple attributes to an element:
\`\`\`html
<button hx-get="/info"
hx-on::before-request="alert('Making a request!')"
hx-on::after-request="alert('Done making a request!')">
Get Info!
</button>
\`\`\`
Finally, in order to make this feature compatible with some templating languages (e.g. [JSX](https://react.dev/learn/writing-markup-with-jsx)) that do not like having a colon (`:`)
in HTML attributes, you may use dashes in the place of colons for both the long form and the shorthand form:
\`\`\`html
<!-- These two are equivalent -->
<button hx-get="/info" hx-on-htmx-before-request="alert('Making a request!')">
Get Info!
</button>
<button hx-get="/info" hx-on--before-request="alert('Making a request!')">
Get Info!
</button>
\`\`\`
### hx-on (deprecated)
The value is an event name, followed by a colon `:`, followed by the script:
\`\`\`html
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')">
Get Info!
</button>
\`\`\`
Multiple handlers can be defined by putting them on new lines:
\`\`\`html
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')
htmx:afterRequest: alert('Done making a request!')">
Get Info!
</button>
\`\`\`
### Symbols
Like `onevent`, two symbols are made available to event handler scripts:
* `this` - The element on which the `hx-on` attribute is defined
* `event` - The event that triggered the handler
### Notes
* `hx-on` is _not_ inherited, however due to
[event bubbling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling_and_capture),
`hx-on` attributes on parent elements will typically be triggered by events on child elements
* `hx-on:*` and `hx-on` cannot be used together on the same element; if `hx-on:*` is present, the value of an `hx-on` attribute
on the same element will be ignored. The two forms can be mixed in the same document, however.
```
# attributes/hx-params.md
```md
+++
title = "hx-params"
description = """\
The hx-params attribute in htmx allows you to filter the parameters that will be submitted with an AJAX request."""
+++
The `hx-params` attribute allows you to filter the parameters that will be submitted with an AJAX request.
The possible values of this attribute are:
* `*` - Include all parameters (default)
* `none` - Include no parameters
* `not <param-list>` - Include all except the comma separated list of parameter names
* `<param-list>` - Include all the comma separated list of parameter names
\`\`\`html
<div hx-get="/example" hx-params="*">Get Some HTML, Including Params</div>
\`\`\`
This div will include all the parameters that a `POST` would, but they will be URL encoded
and included in the URL, as per usual with a `GET`.
## Notes
* `hx-params` is inherited and can be placed on a parent element
```
# attributes/hx-patch.md
```md
+++
title = "hx-patch"
description = """\
The hx-patch attribute in htmx will cause an element to issue a PATCH request to the specified URL and swap the \
returned HTML into the DOM using a swap strategy."""
+++
The `hx-patch` attribute will cause an element to issue a `PATCH` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-patch="/account" hx-target="body">
Patch Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `PATCH` to `/account` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-patch` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
```
# attributes/hx-post.md
```md
+++
title = "hx-post"
description = """\
The hx-post attribute in htmx will cause an element to issue a POST request to the specified URL and swap the \
returned HTML into the DOM using a swap strategy."""
+++
The `hx-post` attribute will cause an element to issue a `POST` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-post="/account/enable" hx-target="body">
Enable Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `POST` to `/account/enable` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-post` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
```
# attributes/hx-preserve.md
```md
+++
title = "hx-preserve"
description = """\
The hx-preserve attribute in htmx allows you to keep an element unchanged during HTML replacement. Elements with \
hx-preserve set are preserved by `id` when htmx updates any ancestor element."""
+++
The `hx-preserve` attribute allows you to keep an element unchanged during HTML replacement.
Elements with `hx-preserve` set are preserved by `id` when htmx updates any ancestor element.
You *must* set an unchanging `id` on elements for `hx-preserve` to work.
The response requires an element with the same `id`, but its type and other attributes are ignored.
## Notes
* `hx-preserve` is not inherited
* You can use `hx-preserve="true"` or use it as a boolean attribute with just `hx-preserve`
* Some elements cannot unfortunately be preserved properly, such as `<input type="text">` (focus and caret position are lost), iframes or certain types of videos. To tackle some of these cases we recommend the [morphdom extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md), which does a more elaborate DOM
reconciliation
* When using [History Support](@/docs.md#history) for actions like the back button `hx-preserve` elements will also have their state preserved
* Avoid using [hx-swap](@/attributes/hx-swap.md) set to `none` with requests that could contain a `hx-preserve` element to avoid losing it
* `hx-preserve` can cause elements to be removed from their current location and relocated to a new location when swapping in a partial/oob response
\`\`\`html
<div id="new_location">
Just relocated the video here
<div id="video" hx-preserve></div>
</div>
\`\`\`
* Can be used on the inside content of a [hx-swap-oob](@/attributes/hx-swap-oob.md) element
\`\`\`html
<div id="notify" hx-swap-oob="true">
Notification updated but keep the same retain
<div id="retain" hx-preserve></div>
</div>
\`\`\`
```
# attributes/hx-prompt.md
```md
+++
title = "hx-prompt"
description = """\
The hx-prompt attribute in htmx allows you to show a prompt before issuing a request. The value of the prompt will \
be included in the request in the `HX-Prompt` header."""
+++
The `hx-prompt` attribute allows you to show a prompt before issuing a request. The value of
the prompt will be included in the request in the `HX-Prompt` header.
Here is an example:
\`\`\`html
<button hx-delete="/account" hx-prompt="Enter your account name to confirm deletion">
Delete My Account
</button>
\`\`\`
## Notes
* `hx-prompt` is inherited and can be placed on a parent element
```
# attributes/hx-push-url.md
```md
+++
title = "hx-push-url"
description = """\
The hx-push-url attribute in htmx allows you to push a URL into the browser location history. This creates a new \
history entry, allowing navigation with the browser's back and forward buttons."""
+++
The `hx-push-url` attribute allows you to push a URL into the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
This creates a new history entry, allowing navigation with the browser’s back and forward buttons.
htmx snapshots the current DOM and saves it into its history cache, and restores from this cache on navigation.
The possible values of this attribute are:
1. `true`, which pushes the fetched URL into history.
2. `false`, which disables pushing the fetched URL if it would otherwise be pushed due to inheritance or [`hx-boost`](/attributes/hx-boost).
3. A URL to be pushed into the location bar.
This may be relative or absolute, as per [`history.pushState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState).
Here is an example:
\`\`\`html
<div hx-get="/account" hx-push-url="true">
Go to My Account
</div>
\`\`\`
This will cause htmx to snapshot the current DOM to `localStorage` and push the URL `/account' into the browser location bar.
Another example:
\`\`\`html
<div hx-get="/account" hx-push-url="/account/home">
Go to My Account
</div>
\`\`\`
This will push the URL `/account/home' into the location history.
## Notes
* `hx-push-url` is inherited and can be placed on a parent element
* The [`HX-Push-Url` response header](@/headers/hx-push-url.md) has similar behavior and can override this attribute.
* The [`hx-history-elt` attribute](@/attributes/hx-history-elt.md) allows changing which element is saved in the history cache.
```
# attributes/hx-put.md
```md
+++
title = "hx-put"
description = """\
The hx-put attribute in htmx will cause an element to issue a PUT request to the specified URL and swap the returned \
HTML into the DOM using a swap strategy."""
+++
The `hx-put` attribute will cause an element to issue a `PUT` to the specified URL and swap
the HTML into the DOM using a swap strategy:
\`\`\`html
<button hx-put="/account" hx-target="body">
Put Money In Your Account
</button>
\`\`\`
This example will cause the `button` to issue a `PUT` to `/account` and swap the returned HTML into
the `innerHTML` of the `body`.
## Notes
* `hx-put` is not inherited
* You can control the target of the swap using the [hx-target](@/attributes/hx-target.md) attribute
* You can control the swap strategy by using the [hx-swap](@/attributes/hx-swap.md) attribute
* You can control what event triggers the request with the [hx-trigger](@/attributes/hx-trigger.md) attribute
* You can control the data submitted with the request in various ways, documented here: [Parameters](@/docs.md#parameters)
```
# attributes/hx-replace-url.md
```md
+++
title = "hx-replace-url"
description = """\
The hx-replace-url attribute in htmx allows you to replace the current URL of the browser location history."""
+++
The `hx-replace-url` attribute allows you to replace the current url of the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
The possible values of this attribute are:
1. `true`, which replaces the fetched URL in the browser navigation bar.
2. `false`, which disables replacing the fetched URL if it would otherwise be replaced due to inheritance.
3. A URL to be replaced into the location bar.
This may be relative or absolute, as per [`history.replaceState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState).
Here is an example:
\`\`\`html
<div hx-get="/account" hx-replace-url="true">
Go to My Account
</div>
\`\`\`
This will cause htmx to snapshot the current DOM to `localStorage` and replace the URL `/account' in the browser location bar.
Another example:
\`\`\`html
<div hx-get="/account" hx-replace-url="/account/home">
Go to My Account
</div>
\`\`\`
This will replace the URL `/account/home' in the browser location bar.
## Notes
* `hx-replace-url` is inherited and can be placed on a parent element
* The [`HX-Replace-Url` response header](@/headers/hx-replace-url.md) has similar behavior and can override this attribute.
* The [`hx-history-elt` attribute](@/attributes/hx-history-elt.md) allows changing which element is saved in the history cache.
* The [`hx-push-url` attribute](@/attributes/hx-push-url.md) is a similar and more commonly used attribute, which creates a
new history entry rather than replacing the current one.
```
# attributes/hx-request.md
```md
+++
title = "hx-request"
description = """\
The hx-request attribute in htmx allows you to configure the request timeout, whether the request will send \
credentials, and whether the request will include headers."""
+++
The `hx-request` attribute allows you to configure various aspects of the request via the following attributes:
* `timeout` - the timeout for the request, in milliseconds
* `credentials` - if the request will send credentials
* `noHeaders` - strips all headers from the request
These attributes are set using a JSON-like syntax:
\`\`\`html
<div ... hx-request='{"timeout":100}'>
...
</div>
\`\`\`
You may make the values dynamically evaluated by adding the `javascript:` or `js:` prefix:
\`\`\`html
<div ... hx-request='js: timeout:getTimeoutSetting() '>
...
</div>
\`\`\`
## Notes
* `hx-request` is merge-inherited and can be placed on a parent element
```
# attributes/hx-select-oob.md
```md
+++
title = "hx-select-oob"
description = """\
The hx-select-oob attribute in htmx allows you to select content from a response to be swapped in via an out-of-band \
swap. The value of this attribute is comma separated list of elements to be swapped out of band."""
+++
The `hx-select-oob` attribute allows you to select content from a response to be swapped in via an out-of-band swap.
The value of this attribute is comma separated list of elements to be swapped out of band. This attribute is almost
always paired with [hx-select](@/attributes/hx-select.md).
Here is an example that selects a subset of the response content:
\`\`\`html
<div>
<div id="alert"></div>
<button hx-get="/info"
hx-select="#info-details"
hx-swap="outerHTML"
hx-select-oob="#alert">
Get Info!
</button>
</div>
\`\`\`
This button will issue a `GET` to `/info` and then select the element with the id `info-details`,
which will replace the entire button in the DOM, and, in addition, pick out an element with the id `alert`
in the response and swap it in for div in the DOM with the same ID.
Each value in the comma separated list of values can specify any valid [`hx-swap`](@/attributes/hx-swap.md)
strategy by separating the selector and the swap strategy with a `:`, with the strategy otherwise defaulting to `outerHTML`.
For example, to prepend the alert content instead of replacing it:
\`\`\`html
<div>
<div id="alert"></div>
<button hx-get="/info"
hx-select="#info-details"
hx-swap="outerHTML"
hx-select-oob="#alert:afterbegin">
Get Info!
</button>
</div>
\`\`\`
## Notes
* `hx-select-oob` is inherited and can be placed on a parent element
```
# attributes/hx-select.md
```md
+++
title = "hx-select"
description = "The hx-select attribute in htmx allows you to select the content you want swapped from a response."
+++
The `hx-select` attribute allows you to select the content you want swapped from a response. The value of
this attribute is a CSS query selector of the element or elements to select from the response.
Here is an example that selects a subset of the response content:
\`\`\`html
<div>
<button hx-get="/info" hx-select="#info-detail" hx-swap="outerHTML">
Get Info!
</button>
</div>
\`\`\`
So this button will issue a `GET` to `/info` and then select the element with the id `info-detail`,
which will replace the entire button in the DOM.
## Notes
* `hx-select` is inherited and can be placed on a parent element
```
# attributes/hx-swap-oob.md
```md
+++
title = "hx-swap-oob"
description = """\
The hx-swap-oob attribute in htmx allows you to specify that some content in a response should be swapped into the \
DOM somewhere other than the target, that is 'out-of-band'. This allows you to piggyback updates to other elements \
on a response."""
+++
The `hx-swap-oob` attribute allows you to specify that some content in a response should be
swapped into the DOM somewhere other than the target, that is "Out of Band". This allows you to piggyback updates to other element updates on a response.
Consider the following response HTML:
\`\`\`html
<div>
...
</div>
<div id="alerts" hx-swap-oob="true">
Saved!
</div>
\`\`\`
The first div will be swapped into the target the usual manner. The second div, however, will be swapped in as a replacement for the element with the id `alerts`, and will not end up in the target.
The value of the `hx-swap-oob` can be:
* `true`
* any valid [`hx-swap`](@/attributes/hx-swap.md) value
* any valid [`hx-swap`](@/attributes/hx-swap.md) value, followed by a colon, followed by a CSS selector
If the value is `true` or `outerHTML` (which are equivalent) the element will be swapped inline.
If a swap value is given, that swap strategy will be used and the encapsulating tag pair will be stripped for all strategies other than `outerHTML`.
If a selector is given, all elements matched by that selector will be swapped. If not, the element with an ID matching the new content will be swapped.
### Using alternate swap strategies
As mentioned previously when using swap strategies other than `true` or `outerHTML` the encapsulating tags are stripped, as such you need to excapsulate the returned data with the correct tags for the context.
When trying to insert a `<tr>` in a table that uses `<tbody>`:
\`\`\`html
<tbody hx-swap-oob="beforeend:#table tbody">
<tr>
...
</tr>
</tbody>
\`\`\`
A "plain" table:
\`\`\`html
<table hx-swap-oob="beforeend:#table2">
<tr>
...
</tr>
</table>
\`\`\`
A `<li>` may be encapsulated in `<ul>`, `<ol>`, `<div>` or `<span>`, for example:
\`\`\`html
<ul hx-swap-oob="beforeend:#list1">
<li>...</li>
</ul>
\`\`\`
A `<p>` can be encapsulated in `<div>` or `<span>`:
\`\`\`html
<span hx-swap-oob="beforeend:#text">
<p>...</p>
</span>
\`\`\`
### Troublesome Tables and lists
Note that you can use a `template` tag to encapsulate types of elements that, by the HTML spec, can't stand on their own in the
DOM (`<tr>`, `<td>`, `<th>`, `<thead>`, `<tbody>`, `<tfoot>`, `<colgroup>`, `<caption>`, `<col>` & `<li>`).
Here is an example with an out-of-band swap of a table row being encapsulated in this way:
\`\`\`html
<div>
...
</div>
<template>
<tr id="row" hx-swap-oob="true">
...
</tr>
</template>
\`\`\`
Note that these template tags will be removed from the final content of the page.
### Slippery SVGs
Some element types, like SVG, use a specific XML namespace for their child elements. This prevents internal elements from working correctly when swapped in, unless they are encapsulated within a `svg` tag. To modify the internal contents of an existing SVG, you can use both `template` and `svg` tags to encapsulate the elements, allowing them to get the correct namespace applied.
Here is an example with an out-of-band swap of svg elements being encapsulated in this way:
\`\`\`html
<div>
...
</div>
<template><svg>
<circle hx-swap-oob="true" id="circle1" r="35" cx="50" cy="50" fill="red" />
</svg></template>
<template><svg hx-swap-oob="beforebegin:#circle1">
<circle id="circle2" r="45" cx="50" cy="50" fill="blue" />
</svg></template>
\`\`\`
This will replace circle1 inline and then insert circle2 before circle1.
Note that these `template` and `svg` wrapping tags will be removed from the final content of the page.
## Nested OOB Swaps
By default, any element with `hx-swap-oob=` attribute anywhere in the response is processed for oob swap behavior, including when an element is nested within the main response element.
This can be problematic when using [template fragments](https://htmx.org/essays/template-fragments/) where a fragment may be reused as an oob-swap target and also as part of a bigger fragment. When the bigger fragment is the main response the inner fragment will still be processed as an oob swap, removing it from the dom.
This behavior can be changed by setting the config `htmx.config.allowNestedOobSwaps` to `false`. If this config option is `false`, OOB swaps are only processed when the element is *adjacent to* the main response element, OOB swaps elsewhere will be ignored and oob-swap-related attributes stripped.
## Notes
* `hx-swap-oob` is not inherited
```
# attributes/hx-swap.md
```md
+++
title = "hx-swap"
description = """\
The hx-swap attribute in htmx allows you to specify the 'swap strategy', or how the response will be swapped in \
relative to the target of an AJAX request. The default swap strategy is `innerHTML`."""
+++
The `hx-swap` attribute allows you to specify how the response will be swapped in relative to the
[target](@/attributes/hx-target.md) of an AJAX request. If you do not specify the option, the default is
`htmx.config.defaultSwapStyle` (`innerHTML`).
The possible values of this attribute are:
* `innerHTML` - Replace the inner html of the target element
* `outerHTML` - Replace the entire target element with the response
* `textContent` - Replace the text content of the target element, without parsing the response as HTML
* `beforebegin` - Insert the response before the target element
* `afterbegin` - Insert the response before the first child of the target element
* `beforeend` - Insert the response after the last child of the target element
* `afterend` - Insert the response after the target element
* `delete` - Deletes the target element regardless of the response
* `none`- Does not append content from response (out of band items will still be processed).
These options are based on standard DOM naming and the
[`Element.insertAdjacentHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML)
specification.
So in this code:
\`\`\`html
<div hx-get="/example" hx-swap="afterend">Get Some HTML & Append It</div>
\`\`\`
The `div` will issue a request to `/example` and append the returned content after the `div`
### Modifiers
The `hx-swap` attributes supports modifiers for changing the behavior of the swap. They are outlined below.
#### Transition: `transition`
If you want to use the new [View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API
when a swap occurs, you can use the `transition:true` option for your swap. You can also enable this feature globally by
setting the `htmx.config.globalViewTransitions` config setting to `true`.
#### Timing: `swap` & `settle`
You can modify the amount of time that htmx will wait after receiving a response to swap the content
by including a `swap` modifier:
\`\`\`html
<!-- this will wait 1s before doing the swap after it is received -->
<div hx-get="/example" hx-swap="innerHTML swap:1s">Get Some HTML & Append It</div>
\`\`\`
Similarly, you can modify the time between the swap and the settle logic by including a `settle`
modifier:
\`\`\`html
<!-- this will wait 1s before doing the settle after it is received -->
<div hx-get="/example" hx-swap="innerHTML settle:1s">Get Some HTML & Append It</div>
\`\`\`
These attributes can be used to synchronize htmx with the timing of CSS transition effects.
#### Title: `ignoreTitle`
By default, htmx will update the title of the page if it finds a `<title>` tag in the response content. You can turn
off this behavior by setting the `ignoreTitle` option to true.
#### Scrolling: `scroll` & `show`
You can also change the scrolling behavior of the target element by using the `scroll` and `show` modifiers, both
of which take the values `top` and `bottom`:
\`\`\`html
<!-- this fixed-height div will scroll to the bottom of the div after content is appended -->
<div style="height:200px; overflow: scroll"
hx-get="/example"
hx-swap="beforeend scroll:bottom">
Get Some HTML & Append It & Scroll To Bottom
</div>
\`\`\`
\`\`\`html
<!-- this will get some content and add it to #another-div, then ensure that the top of #another-div is visible in the
viewport -->
<div hx-get="/example"
hx-swap="innerHTML show:top"
hx-target="#another-div">
Get Some Content
</div>
\`\`\`
If you wish to target a different element for scrolling or showing, you may place a CSS selector after the `scroll:`
or `show:`, followed by `:top` or `:bottom`:
\`\`\`html
<!-- this will get some content and swap it into the current div, then ensure that the top of #another-div is visible in the
viewport -->
<div hx-get="/example"
hx-swap="innerHTML show:#another-div:top">
Get Some Content
</div>
\`\`\`
You may also use `window:top` and `window:bottom` to scroll to the top and bottom of the current window.
\`\`\`html
<!-- this will get some content and swap it into the current div, then ensure that the viewport is scrolled to the
very top -->
<div hx-get="/example"
hx-swap="innerHTML show:window:top">
Get Some Content
</div>
\`\`\`
For boosted links and forms the default behaviour is `show:top`. You can disable it globally with
[htmx.config.scrollIntoViewOnBoost](@/api.md#config) or you can use `hx-swap="show:none"` on an element basis.
\`\`\`html
<form action="/example" hx-swap="show:none">
...
</form>
\`\`\`
#### Focus scroll
htmx preserves focus between requests for inputs that have a defined id attribute. By default htmx prevents auto-scrolling to focused inputs between requests which can be unwanted behavior on longer requests when the user has already scrolled away. To enable focus scroll you can use `focus-scroll:true`.
\`\`\`html
<input id="name" hx-get="/validation"
hx-swap="outerHTML focus-scroll:true"/>
\`\`\`
Alternatively, if you want the page to automatically scroll to the focused element after each request you can change the htmx global configuration value `htmx.config.defaultFocusScroll` to true. Then disable it for specific requests using `focus-scroll:false`.
\`\`\`html
<input id="name" hx-get="/validation"
hx-swap="outerHTML focus-scroll:false"/>
\`\`\`
## Notes
* `hx-swap` is inherited and can be placed on a parent element
* The default value of this attribute is `innerHTML`
* Due to DOM limitations, it’s not possible to use the `outerHTML` method on the `<body>` element.
htmx will change `outerHTML` on `<body>` to use `innerHTML`.
* The default swap delay is 0ms
* The default settle delay is 20ms
```
# attributes/hx-sync.md
```md
+++
title = "hx-sync"
description = "The hx-sync attribute in htmx allows you to synchronize AJAX requests between multiple elements."
+++
The `hx-sync` attribute allows you to synchronize AJAX requests between multiple elements.
The `hx-sync` attribute consists of a CSS selector to indicate the element to synchronize on, followed optionally
by a colon and then by an optional syncing strategy. The available strategies are:
* `drop` - drop (ignore) this request if an existing request is in flight (the default)
* `abort` - drop (ignore) this request if an existing request is in flight, and, if that is not the case,
*abort* this request if another request occurs while it is still in flight
* `replace` - abort the current request, if any, and replace it with this request
* `queue` - place this request in the request queue associated with the given element
The `queue` modifier can take an additional argument indicating exactly how to queue:
* `queue first` - queue the first request to show up while a request is in flight
* `queue last` - queue the last request to show up while a request is in flight
* `queue all` - queue all requests that show up while a request is in flight
## Notes
* `hx-sync` is inherited and can be placed on a parent element
This example resolves a race condition between a form's submit request and an individual input's validation request. Normally, without using `hx-sync`, filling out the input and immediately submitting the form triggers two parallel requests to `/validate` and `/store`. Using `hx-sync="closest form:abort"` on the input will watch for requests on the form and abort the input's request if a form request is present or starts while the input request is in flight.
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
\`\`\`
If you'd rather prioritize the validation request over the submit request, you can use the `drop` strategy. This example will prioritize the validation request over the submit request so that if a validation request is in flight, the form cannot be submitted.
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:drop"
>
<button type="submit">Submit</button>
</form>
\`\`\`
When dealing with forms that contain many inputs, you can prioritize the submit request over all input validation requests using the hx-sync `replace` strategy on the form tag. This will cancel any in-flight validation requests and issue only the `hx-post="/store"` request. If you'd rather abort the submit request and prioritize any existing validation requests you can use the `hx-sync="this:abort"` strategy on the form tag.
\`\`\`html
<form hx-post="/store" hx-sync="this:replace">
<input id="title" name="title" type="text" hx-post="/validate" hx-trigger="change" />
<button type="submit">Submit</button>
</form>
\`\`\`
When implementing active search functionality the hx-trigger attribute's `delay` modifier can be used to debounce the user's input and avoid making multiple requests while the user types. However, once a request is made, if the user begins typing again a new request will begin even if the previous one has not finished processing. This example will cancel any in-flight requests and use only the last request. In cases where the search input is contained within the target, then using `hx-sync` like this also helps reduce the chances that the input will be replaced while the user is still typing.
\`\`\`html
<input type="search"
hx-get="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-sync="this:replace">
\`\`\`
```
# attributes/hx-target.md
```md
+++
title = "hx-target"
description = """\
The hx-target attribute in htmx allows you to target a different element for swapping than the one issuing the AJAX \
request."""
+++
The `hx-target` attribute allows you to target a different element for swapping than the one issuing the AJAX
request. The value of this attribute can be:
* A CSS query selector of the element to target.
* `this` which indicates that the element that the `hx-target` attribute is on is the target.
* `closest <CSS selector>` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next` which resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling)
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous .error` will target the closest previous sibling with `error` class)
Here is an example that targets a div:
\`\`\`html
<div>
<div id="response-div"></div>
<button hx-post="/register" hx-target="#response-div" hx-swap="beforeend">
Register!
</button>
</div>
\`\`\`
The response from the `/register` url will be appended to the `div` with the id `response-div`.
This example uses `hx-target="this"` to make a link that updates itself when clicked:
\`\`\`html
<a hx-post="/new-link" hx-target="this" hx-swap="outerHTML">New link</a>
\`\`\`
## Notes
* `hx-target` is inherited and can be placed on a parent element
```
# attributes/hx-trigger.md
```md
+++
title = "hx-trigger"
description = """\
The hx-trigger attribute in htmx allows you to specify what triggers an AJAX request. Supported triggers include \
standard DOM events, custom events, polling intervals, and event modifiers. The hx-trigger attribute also allows \
specifying event filtering, timing controls, event bubbling, and multiple trigger definitions for fine-grained \
control over when and how requests are initiated."""
+++
The `hx-trigger` attribute allows you to specify what triggers an AJAX request. A trigger
value can be one of the following:
* An event name (e.g. "click" or "my-custom-event") followed by an event filter and a set of event modifiers
* A polling definition of the form `every <timing declaration>`
* A comma-separated list of such events
### Standard Events
Standard events refer to [web API events](https://developer.mozilla.org/en-US/docs/Web/API/Element#events) (e.g. click, keydown, mouseup, load).
A standard event, such as `click` can be specified as the trigger like so:
\`\`\`html
<div hx-get="/clicked" hx-trigger="click">Click Me</div>
\`\`\`
#### Standard Event Filters
Events can be filtered by enclosing a boolean javascript expression in square brackets after the event name. If
this expression evaluates to `true` the event will be triggered, otherwise it will be ignored. Standard event filters [require eval](@/docs.md#configuration-options).
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">Control Click Me</div>
\`\`\`
This event will trigger if a click event is triggered with the `event.ctrlKey` property set to true.
Conditions can also refer to global functions or state
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[checkGlobalState()]">Control Click Me</div>
\`\`\`
And can also be combined using the standard javascript syntax
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[ctrlKey&&shiftKey]">Control-Shift Click Me</div>
\`\`\`
Note that all symbols used in the expression will be resolved first against the triggering event, and then next
against the global namespace, so `myEvent[foo]` will first look for a property named `foo` on the event, then look
for a global symbol with the name `foo`
#### Standard Event Modifiers
Standard events can also have modifiers that change how they behave. The modifiers are:
* `once` - the event will only trigger once (e.g. the first click)
* `changed` - the event will only fire if the value of the element has changed. Please pay attention `change` is the name of the event and `changed` is the name of the modifier.
* `delay:<timing declaration>` - a delay will occur before an event triggers a request. If the event
is seen again it will reset the delay.
* `throttle:<timing declaration>` - a throttle will occur after an event triggers a request. If the event
is seen again before the delay completes, it is ignored, the element will trigger at the end of the delay.
* `from:<Extended CSS selector>` - allows the event that triggers a request to come from another element in the document (e.g. listening to a key event on the body, to support hot keys)
* A standard CSS selector resolves to all elements matching that selector. Thus, `from:input` would listen on every input on the page.
* The CSS selector is only evaluated once and is not re-evaluated when the page changes. If you need to detect dynamically added elements use a [standard event filter](#standard-event-filters), for example `hx-trigger="click[event.target.matches('button')] from:body"` which would [catch](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Event_bubbling) click events from every button on the page.
* The extended CSS selector here allows for the following non-standard CSS values:
* `document` - listen for events on the document
* `window` - listen for events on the window
* `closest <CSS selector>` - finds the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest) ancestor element or itself, matching the given css selector
* `find <CSS selector>` - finds the closest child matching the given css selector
* `next` resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling)
* `next <CSS selector>` scans the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous` resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling)
* `previous <CSS selector>` scans the DOM backwards for the first element that matches the given CSS selector.
(e.g. `previous .error` will target the closest previous sibling with `error` class)
* `target:<CSS selector>` - allows you to filter via a CSS selector on the target of the event. This can be useful when you want to listen for
triggers from elements that might not be in the DOM at the point of initialization, by, for example, listening on the body,
but with a target filter for a child element
* `consume` - if this option is included the event will not trigger any other htmx requests on parents (or on elements
listening on parents)
* `queue:<queue option>` - determines how events are queued if an event occurs while a request for another event is in flight. Options are:
* `first` - queue the first event
* `last` - queue the last event (default)
* `all` - queue all events (issue a request for each event)
* `none` - do not queue new events
Here is an example of a search box that searches on `input`, but only if the search value has changed
and the user hasn't typed anything new for 1 second:
\`\`\`html
<input name="q"
hx-get="/search" hx-trigger="input changed delay:1s"
hx-target="#search-results"/>
\`\`\`
The response from the `/search` url will be appended to the `div` with the id `search-results`.
### Non-standard Events
There are some additional non-standard events that htmx supports:
* `load` - triggered on load (useful for lazy-loading something)
* `revealed` - triggered when an element is scrolled into the viewport (also useful for lazy-loading). If you are using `overflow` in css like `overflow-y: scroll` you should use `intersect once` instead of `revealed`.
* `intersect` - fires once when an element first intersects the viewport. This supports two additional options:
* `root:<selector>` - a CSS selector of the root element for intersection
* `threshold:<float>` - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
### Triggering via the `HX-Trigger` header
If you're trying to fire an event from <code>HX-Trigger</code> response header, you will likely want to
use the `from:body` modifier. E.g. if you send a header like this <code>HX-Trigger: my-custom-event</code>
with a response, an element would likely need to look like this:
\`\`\`html
<div hx-get="/example" hx-trigger="my-custom-event from:body">
Triggered by HX-Trigger header...
</div>
\`\`\`
in order to fire.
This is because the header will likely trigger the event in a different DOM hierarchy than the element that you
wish to be triggered. For a similar reason, you will often listen for hot keys from the body.
### Polling
By using the syntax `every <timing declaration>` you can have an element poll periodically:
\`\`\`html
<div hx-get="/latest_updates" hx-trigger="every 1s">
Nothing Yet!
</div>
\`\`\`
This example will issue a `GET` to the `/latest_updates` URL every second and swap the results into
the innerHTML of this div.
If you want to add a filter to polling, it should be added *after* the poll declaration:
\`\`\`html
<div hx-get="/latest_updates" hx-trigger="every 1s [someConditional]">
Nothing Yet!
</div>
\`\`\`
### Multiple Triggers
Multiple triggers can be provided, separated by commas. Each trigger gets its own options.
\`\`\`html
<div hx-get="/news" hx-trigger="load, click delay:1s"></div>
\`\`\`
This example will load `/news` immediately on page load, and then again with a delay of one second after each click.
### Via JavaScript
The AJAX request can be triggered via JavaScript [`htmx.trigger()`](@/api.md#trigger), too.
## Notes
* `hx-trigger` is not inherited
* `hx-trigger` can be used without an AJAX request, in which case it will only fire the `htmx:trigger` event
* In order to pass a CSS selector that contains whitespace (e.g. `form input`) to the `from`- or `target`-modifier, surround the selector in parentheses or curly brackets (e.g. `from:(form input)` or `from:closest (form input)`)
* A reset event in hx-trigger (e.g. hx-trigger="change, reset") might not work as intended, since HTMX builds its values and sends a request before the browser resets the form values. As a workaround, add a delay to let the browser reset the form before making the request (e.g. hx-trigger="change, reset delay:0.01s").
```
# attributes/hx-validate.md
```md
+++
title = "hx-validate"
description = """\
The hx-validate attribute in htmx will cause an element to validate itself using the HTML5 Validation API before it \
submits a request."""
+++
The `hx-validate` attribute will cause an element to validate itself by way of the [HTML5 Validation API](@/docs.md#validation)
before it submits a request.
Only `<form>` elements validate data by default, but other elements do not. Adding `hx-validate="true"` to `<input>`, `<textarea>` or `<select>` enables validation before sending requests.
## Notes
* `hx-validate` is not inherited
```
# attributes/hx-vals.md
```md
+++
title = "hx-vals"
description = """\
The hx-vals attribute in htmx allows you to add to the parameters that will be submitted with an AJAX request."""
+++
The `hx-vals` attribute allows you to add to the parameters that will be submitted with an AJAX request.
By default, the value of this attribute is a list of name-expression values in [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html)
format.
If you wish for `hx-vals` to *evaluate* the values given, you can prefix the values with `javascript:` or `js:`.
\`\`\`html
<div hx-get="/example" hx-vals='{"myVal": "My Value"}'>Get Some HTML, Including A Value in the Request</div>
<div hx-get="/example" hx-vals='js:{myVal: calculateValue()}'>Get Some HTML, Including a Dynamic Value from Javascript in the Request</div>
\`\`\`
When using evaluated code you can access the `event` object. This example includes the value of the last typed key within the input.
\`\`\`html
<div hx-get="/example" hx-trigger="keyup" hx-vals='js:{lastKey: event.key}'>
<input type="text" />
</div>
\`\`\`
You can also use the spread operator to dynamically specify values. This allows you to include all properties from an object returned by a function:
\`\`\`html
<div hx-get="/example" hx-vals='js:{...foo()}'>Get Some HTML, Including All Values from foo() in the Request</div>
\`\`\`
In this example, if `foo()` returns an object like `{name: "John", age: 30}`, both `name` and `age` will be included as parameters in the request.
## Security Considerations
* By default, the value of `hx-vals` must be valid [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON).
It is **not** dynamically computed. If you use the `javascript:` prefix, be aware that you are introducing
security considerations, especially when dealing with user input such as query strings or user-generated content,
which could introduce a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability.
## Notes
* `hx-vals` is inherited and can be placed on a parent element.
* A child declaration of a variable overrides a parent declaration.
* Input values with the same name will be overridden by variable declarations.
```
# attributes/hx-vars.md
```md
+++
title = "hx-vars"
description = """\
The hx-vars attribute in htmx allows you to dynamically add to the parameters that will be submitted with an AJAX \
request. This attribute has been deprecated. We recommend you use the hx-vals attribute that provides the same \
functionality with safer defaults."""
+++
**NOTE: `hx-vars` has been deprecated in favor of [`hx-vals`](@/attributes/hx-vals.md), which is safer by default.**
The `hx-vars` attribute allows you to dynamically add to the parameters that will be submitted with an AJAX request.
The value of this attribute is a comma separated list of `name`:`<expression>` values, the same as the internal
syntax of javascript [Object Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Object_literals).
\`\`\`html
<div hx-get="/example" hx-vars="myVar:computeMyVar()">Get Some HTML, Including A Dynamic Value in the Request</div>
\`\`\`
## Security Considerations
* The expressions in `hx-vars` are dynamically computed which allows you to add JavaScript code that will be executed. Be careful to **never** trust user input in your expressions as this may lead to a [Cross-Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) vulnerability. If you are dealing with user input such as query strings or user-generated content, consider using [hx-vals](@/attributes/hx-vals.md) which is a safer alternative.
## Notes
* `hx-vars` is inherited and can be placed on a parent element.
* A child declaration of a variable overrides a parent declaration.
* Input values with the same name will be overridden by variable declarations.
```
# docs.md
```md
+++
title = "Documentation"
[extra]
custom_classes = "wide-content"
+++
<div class="row">
<div class="2 col nav">
**Contents**
<div id="contents">
* [introduction](#introduction)
* [installing](#installing)
* [ajax](#ajax)
* [triggers](#triggers)
* [trigger modifiers](#trigger-modifiers)
* [trigger filters](#trigger-filters)
* [special events](#special-events)
* [polling](#polling)
* [load polling](#load_polling)
* [indicators](#indicators)
* [targets](#targets)
* [swapping](#swapping)
* [synchronization](#synchronization)
* [css transitions](#css_transitions)
* [out of band swaps](#oob_swaps)
* [parameters](#parameters)
* [confirming](#confirming)
* [inheritance](#inheritance)
* [boosting](#boosting)
* [websockets & SSE](#websockets-and-sse)
* [history](#history)
* [requests & responses](#requests)
* [validation](#validation)
* [animations](#animations)
* [extensions](#extensions)
* [events & logging](#events)
* [debugging](#debugging)
* [scripting](#scripting)
* [hx-on attribute](#hx-on)
* [3rd party integration](#3rd-party)
* [Web Components](#web-components)
* [caching](#caching)
* [security](#security)
* [configuring](#config)
</div>
</div>
<div class="10 col">
## htmx in a Nutshell {#introduction}
htmx is a library that allows you to access modern browser features directly from HTML, rather than using
javascript.
To understand htmx, first let's take a look at an anchor tag:
\`\`\`html
<a href="/blog">Blog</a>
\`\`\`
This anchor tag tells a browser:
> "When a user clicks on this link, issue an HTTP GET request to '/blog' and load the response content
> into the browser window".
With that in mind, consider the following bit of HTML:
\`\`\`html
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML">
Click Me!
</button>
\`\`\`
This tells htmx:
> "When a user clicks on this button, issue an HTTP POST request to '/clicked' and use the content from the response
> to replace the element with the id `parent-div` in the DOM"
htmx extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly
within the language:
* Now any element, not just anchors and forms, can issue an HTTP request
* Now any event, not just clicks or form submissions, can trigger requests
* Now any [HTTP verb](https://en.wikipedia.org/wiki/HTTP_Verbs), not just `GET` and `POST`, can be used
* Now any element, not just the entire window, can be the target for update by the request
Note that when you are using htmx, on the server side you typically respond with *HTML*, not *JSON*. This keeps you firmly
within the [original web programming model](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm),
using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)
without even needing to really understand that concept.
It's worth mentioning that, if you prefer, you can use the [`data-`](https://html.spec.whatwg.org/multipage/dom.html#attr-data-*) prefix when using htmx:
\`\`\`html
<a data-hx-post="/click">Click Me!</a>
\`\`\`
If you understand the concepts around htmx and want to see the quirks of the library, please see our
[QUIRKS](@/QUIRKS.md) page.
## 1.x to 2.x Migration Guide
[Version 1](https://v1.htmx.org) of htmx is still supported and supports IE11, but the latest version of htmx is 2.x.
If you are migrating to htmx 2.x from [htmx 1.x](https://v1.htmx.org), please see the [htmx 1.x migration guide](@/migration-guide-htmx-1.md).
If you are migrating to htmx from intercooler.js, please see the [intercooler migration guide](@/migration-guide-intercooler.md).
## Installing
Htmx is a dependency-free, browser-oriented javascript library. This means that using it is as simple as adding a `<script>`
tag to your document head. There is no need for a build system to use it.
### Via A CDN (e.g. unpkg.com)
The fastest way to get going with htmx is to load it via a CDN. You can simply add this to
your head tag and get going:
\`\`\`html
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
\`\`\`
An unminified version is also available as well:
\`\`\`html
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.js" integrity="sha384-oeUn82QNXPuVkGCkcrInrS1twIxKhkZiFfr2TdiuObZ3n3yIeMiqcRzkIcguaof1" crossorigin="anonymous"></script>
\`\`\`
While the CDN approach is extremely simple, you may want to consider
[not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn).
### Download a copy
The next easiest way to install htmx is to simply copy it into your project.
Download `htmx.min.js` [from unpkg.com](https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js) and add it to the appropriate directory in your project
and include it where necessary with a `<script>` tag:
\`\`\`html
<script src="/path/to/htmx.min.js"></script>
\`\`\`
### npm
For npm-style build systems, you can install htmx via [npm](https://www.npmjs.com/):
\`\`\`sh
npm install htmx.org@2.0.4
\`\`\`
After installing, you’ll need to use appropriate tooling to use `node_modules/htmx.org/dist/htmx.js` (or `.min.js`).
For example, you might bundle htmx with some extensions and project-specific code.
### Webpack
If you are using webpack to manage your javascript:
* Install `htmx` via your favourite package manager (like npm or yarn)
* Add the import to your `index.js`
\`\`\`js
import 'htmx.org';
\`\`\`
If you want to use the global `htmx` variable (recommended), you need to inject it to the window scope:
* Create a custom JS file
* Import this file to your `index.js` (below the import from step 2)
\`\`\`js
import 'path/to/my_custom.js';
\`\`\`
* Then add this code to the file:
\`\`\`js
window.htmx = require('htmx.org');
\`\`\`
* Finally, rebuild your bundle
## AJAX
The core of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML:
| Attribute | Description |
|----------------------------------------|--------------------------------------------|
| [hx-get](@/attributes/hx-get.md) | Issues a `GET` request to the given URL |
| [hx-post](@/attributes/hx-post.md) | Issues a `POST` request to the given URL |
| [hx-put](@/attributes/hx-put.md) | Issues a `PUT` request to the given URL |
| [hx-patch](@/attributes/hx-patch.md) | Issues a `PATCH` request to the given URL |
| [hx-delete](@/attributes/hx-delete.md) | Issues a `DELETE` request to the given URL |
Each of these attributes takes a URL to issue an AJAX request to. The element will issue a request of the specified
type to the given URL when the element is [triggered](#triggers):
\`\`\`html
<button hx-put="/messages">
Put To Messages
</button>
\`\`\`
This tells the browser:
> When a user clicks on this button, issue a PUT request to the URL /messages and load the response into the button
### Triggering Requests {#triggers}
By default, AJAX requests are triggered by the "natural" event of an element:
* `input`, `textarea` & `select` are triggered on the `change` event
* `form` is triggered on the `submit` event
* everything else is triggered by the `click` event
If you want different behavior you can use the [hx-trigger](@/attributes/hx-trigger.md)
attribute to specify which event will cause the request.
Here is a `div` that posts to `/mouse_entered` when a mouse enters it:
\`\`\`html
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
\`\`\`
#### Trigger Modifiers
A trigger can also have a few additional modifiers that change its behavior. For example, if you want a request to only
happen once, you can use the `once` modifier for the trigger:
\`\`\`html
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[Here Mouse, Mouse!]
</div>
\`\`\`
Other modifiers you can use for triggers are:
* `changed` - only issue a request if the value of the element has changed
* `delay:<time interval>` - wait the given amount of time (e.g. `1s`) before
issuing the request. If the event triggers again, the countdown is reset.
* `throttle:<time interval>` - wait the given amount of time (e.g. `1s`) before
issuing the request. Unlike `delay` if a new event occurs before the time limit is hit the event will be discarded,
so the request will trigger at the end of the time period.
* `from:<CSS Selector>` - listen for the event on a different element. This can be used for things like keyboard shortcuts. Note that this CSS selector is not re-evaluated if the page changes.
You can use these attributes to implement many common UX patterns, such as [Active Search](@/examples/active-search.md):
\`\`\`html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search...">
<div id="search-results"></div>
\`\`\`
This input will issue a request 500 milliseconds after a key up event if the input has been changed and inserts the results
into the `div` with the id `search-results`.
Multiple triggers can be specified in the [hx-trigger](@/attributes/hx-trigger.md) attribute, separated by commas.
#### Trigger Filters
You may also apply trigger filters by using square brackets after the event name, enclosing a javascript expression that
will be evaluated. If the expression evaluates to `true` the event will trigger, otherwise it will not.
Here is an example that triggers only on a Control-Click of the element
\`\`\`html
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Control Click Me
</div>
\`\`\`
Properties like `ctrlKey` will be resolved against the triggering event first, then against the global scope. The
`this` symbol will be set to the current element.
#### Special Events
htmx provides a few special events for use in [hx-trigger](@/attributes/hx-trigger.md):
* `load` - fires once when the element is first loaded
* `revealed` - fires once when an element first scrolls into the viewport
* `intersect` - fires once when an element first intersects the viewport. This supports two additional options:
* `root:<selector>` - a CSS selector of the root element for intersection
* `threshold:<float>` - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
You can also use custom events to trigger requests if you have an advanced use case.
#### Polling
If you want an element to poll the given URL rather than wait for an event, you can use the `every` syntax
with the [`hx-trigger`](@/attributes/hx-trigger.md) attribute:
\`\`\`html
<div hx-get="/news" hx-trigger="every 2s"></div>
\`\`\`
This tells htmx
> Every 2 seconds, issue a GET to /news and load the response into the div
If you want to stop polling from a server response you can respond with the HTTP response code [`286`](https://en.wikipedia.org/wiki/86_(term))
and the element will cancel the polling.
#### Load Polling {#load_polling}
Another technique that can be used to achieve polling in htmx is "load polling", where an element specifies
a `load` trigger along with a delay, and replaces itself with the response:
\`\`\`html
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML">
</div>
\`\`\`
If the `/messages` end point keeps returning a div set up this way, it will keep "polling" back to the URL every
second.
Load polling can be useful in situations where a poll has an end point at which point the polling terminates, such as
when you are showing the user a [progress bar](@/examples/progress-bar.md).
### Request Indicators {#indicators}
When an AJAX request is issued it is often good to let the user know that something is happening since the browser
will not give them any feedback. You can accomplish this in htmx by using `htmx-indicator` class.
The `htmx-indicator` class is defined so that the opacity of any element with this class is 0 by default, making it invisible
but present in the DOM.
When htmx issues a request, it will put a `htmx-request` class onto an element (either the requesting element or
another element, if specified). The `htmx-request` class will cause a child element with the `htmx-indicator` class
on it to transition to an opacity of 1, showing the indicator.
\`\`\`html
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
\`\`\`
Here we have a button. When it is clicked the `htmx-request` class will be added to it, which will reveal the spinner
gif element. (I like [SVG spinners](http://samherbert.net/svg-loaders/) these days.)
While the `htmx-indicator` class uses opacity to hide and show the progress indicator, if you would prefer another mechanism
you can create your own CSS transition like so:
\`\`\`css
.htmx-indicator{
display:none;
}
.htmx-request .htmx-indicator{
display:inline;
}
.htmx-request.htmx-indicator{
display:inline;
}
\`\`\`
If you want the `htmx-request` class added to a different element, you can use the [hx-indicator](@/attributes/hx-indicator.md)
attribute with a CSS selector to do so:
\`\`\`html
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
\`\`\`
Here we call out the indicator explicitly by id. Note that we could have placed the class on the parent `div` as well
and had the same effect.
You can also add the [`disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled) to
elements for the duration of a request by using the [hx-disabled-elt](@/attributes/hx-disabled-elt.md) attribute.
### Targets
If you want the response to be loaded into a different element other than the one that made the request, you can
use the [hx-target](@/attributes/hx-target.md) attribute, which takes a CSS selector. Looking back at our Live Search example:
\`\`\`html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search...">
<div id="search-results"></div>
\`\`\`
You can see that the results from the search are going to be loaded into `div#search-results`, rather than into the
input tag.
#### Extended CSS Selectors {#extended-css-selectors}
`hx-target`, and most attributes that take a CSS selector, support an "extended" CSS syntax:
* You can use the `this` keyword, which indicates that the element that the `hx-target` attribute is on is the target
* The `closest <CSS selector>` syntax will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector.
(e.g. `closest tr` will target the closest table row to the element)
* The `next <CSS selector>` syntax will find the next element in the DOM matching the given CSS selector.
* The `previous <CSS selector>` syntax will find the previous element in the DOM matching the given CSS selector.
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
(e.g `find tr` would target the first child descendant row to the element)
In addition, a CSS selector may be wrapped in `<` and `/>` characters, mimicking the
[query literal](https://hyperscript.org/expressions/query-reference/) syntax of hyperscript.
Relative targets like this can be useful for creating flexible user interfaces without peppering your DOM with lots
of `id` attributes.
### Swapping {#swapping}
htmx offers a few different ways to swap the HTML returned into the DOM. By default, the content replaces the
`innerHTML` of the target element. You can modify this by using the [hx-swap](@/attributes/hx-swap.md) attribute
with any of the following values:
| Name | Description
|------|-------------
| `innerHTML` | the default, puts the content inside the target element
| `outerHTML` | replaces the entire target element with the returned content
| `afterbegin` | prepends the content before the first child inside the target
| `beforebegin` | prepends the content before the target in the target's parent element
| `beforeend` | appends the content after the last child inside the target
| `afterend` | appends the content after the target in the target's parent element
| `delete` | deletes the target element regardless of the response
| `none` | does not append content from response ([Out of Band Swaps](#oob_swaps) and [Response Headers](#response-headers) will still be processed)
#### Morph Swaps {#morphing}
In addition to the standard swap mechanisms above, htmx also supports _morphing_ swaps, via extensions. Morphing swaps
attempt to _merge_ new content into the existing DOM, rather than simply replacing it. They often do a better job
preserving things like focus, video state, etc. by mutating existing nodes in-place during the swap operation, at the
cost of more CPU.
The following extensions are available for morph-style swaps:
* [Idiomorph](/extensions/idiomorph) - A morphing algorithm created by the htmx developers.
* [Morphdom Swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) - Based on the [morphdom](https://github.com/patrick-steele-idem/morphdom),
the original DOM morphing library.
* [Alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) - Based on the [alpine morph](https://alpinejs.dev/plugins/morph) plugin, plays
well with alpine.js
#### View Transitions {#view-transitions}
The new, experimental [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
gives developers a way to create an animated transition between different DOM states. It is still in active development
and is not available in all browsers, but htmx provides a way to work with this new API that falls back to the non-transition
mechanism if the API is not available in a given browser.
You can experiment with this new API using the following approaches:
* Set the `htmx.config.globalViewTransitions` config variable to `true` to use transitions for all swaps
* Use the `transition:true` option in the `hx-swap` attribute
* If an element swap is going to be transitioned due to either of the above configurations, you may catch the
`htmx:beforeTransition` event and call `preventDefault()` on it to cancel the transition.
View Transitions can be configured using CSS, as outlined in [the Chrome documentation for the feature](https://developer.chrome.com/docs/web-platform/view-transitions/#simple-customization).
You can see a view transition example on the [Animation Examples](/examples/animations#view-transitions) page.
#### Swap Options
The [hx-swap](@/attributes/hx-swap.md) attribute supports many options for tuning the swapping behavior of htmx. For
example, by default htmx will swap in the title of a title tag found anywhere in the new content. You can turn this
behavior off by setting the `ignoreTitle` modifier to true:
\`\`\`html
<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">Like</button>
\`\`\`
The modifiers available on `hx-swap` are:
| Option | Description |
|---------------|----------------------------------------------------------------------------------------------------------|
| `transition` | `true` or `false`, whether to use the view transition API for this swap |
| `swap` | The swap delay to use (e.g. `100ms`) between when old content is cleared and the new content is inserted |
| `settle` | The settle delay to use (e.g. `100ms`) between when new content is inserted and when it is settled |
| `ignoreTitle` | If set to `true`, any title found in the new content will be ignored and not update the document title |
| `scroll` | `top` or `bottom`, will scroll the target element to its top or bottom |
| `show` | `top` or `bottom`, will scroll the target element's top or bottom into view |
All swap modifiers appear after the swap style is specified, and are colon-separated.
See the [hx-swap](@/attributes/hx-swap.md) documentation for more details on these options.
### Synchronization {#synchronization}
Often you want to coordinate the requests between two elements. For example, you may want a request from one element
to supersede the request of another element, or to wait until the other element's request has finished.
htmx offers a [`hx-sync`](@/attributes/hx-sync.md) attribute to help you accomplish this.
Consider a race condition between a form submission and an individual input's validation request in this HTML:
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change">
<button type="submit">Submit</button>
</form>
\`\`\`
Without using `hx-sync`, filling out the input and immediately submitting the form triggers two parallel requests to
`/validate` and `/store`.
Using `hx-sync="closest form:abort"` on the input will watch for requests on the form and abort the input's request if
a form request is present or starts while the input request is in flight:
\`\`\`html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
\`\`\`
This resolves the synchronization between the two elements in a declarative way.
htmx also supports a programmatic way to cancel requests: you can send the `htmx:abort` event to an element to
cancel any in-flight requests:
\`\`\`html
<button id="request-button" hx-post="/example">
Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Request
</button>
\`\`\`
More examples and details can be found on the [`hx-sync` attribute page.](@/attributes/hx-sync.md)
### CSS Transitions {#css_transitions}
htmx makes it easy to use [CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) without
javascript. Consider this HTML content:
\`\`\`html
<div id="div1">Original Content</div>
\`\`\`
Imagine this content is replaced by htmx via an ajax request with this new content:
\`\`\`html
<div id="div1" class="red">New Content</div>
\`\`\`
Note two things:
* The div has the *same* id in the original and in the new content
* The `red` class has been added to the new content
Given this situation, we can write a CSS transition from the old state to the new state:
\`\`\`css
.red {
color: red;
transition: all ease-in 1s ;
}
\`\`\`
When htmx swaps in this new content, it will do so in such a way that the CSS transition will apply to the new content,
giving you a nice, smooth transition to the new state.
So, in summary, all you need to do to use CSS transitions for an element is keep its `id` stable across requests!
You can see the [Animation Examples](@/examples/animations.md) for more details and live demonstrations.
#### Details
To understand how CSS transitions actually work in htmx, you must understand the underlying swap & settle model that htmx uses.
When new content is received from a server, before the content is swapped in, the existing
content of the page is examined for elements that match by the `id` attribute. If a match
is found for an element in the new content, the attributes of the old content are copied
onto the new element before the swap occurs. The new content is then swapped in, but with the
*old* attribute values. Finally, the new attribute values are swapped in, after a "settle" delay
(20ms by default). A little crazy, but this is what allows CSS transitions to work without any javascript by
the developer.
### Out of Band Swaps {#oob_swaps}
If you want to swap content from a response directly into the DOM by using the `id` attribute you can use the
[hx-swap-oob](@/attributes/hx-swap-oob.md) attribute in the *response* html:
\`\`\`html
<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content
\`\`\`
In this response, `div#message` would be swapped directly into the matching DOM element, while the additional content
would be swapped into the target in the normal manner.
You can use this technique to "piggy-back" updates on other requests.
#### Troublesome Tables
Table elements can be problematic when combined with out of band swaps, because, by the HTML spec, many can't stand on
their own in the DOM (e.g. `<tr>` or `<td>`).
To avoid this issue you can use a `template` tag to encapsulate these elements:
\`\`\`html
<template>
<tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>
\`\`\`
#### Selecting Content To Swap
If you want to select a subset of the response HTML to swap into the target, you can use the [hx-select](@/attributes/hx-select.md)
attribute, which takes a CSS selector and selects the matching elements from the response.
You can also pick out pieces of content for an out-of-band swap by using the [hx-select-oob](@/attributes/hx-select-oob.md)
attribute, which takes a list of element IDs to pick out and swap.
#### Preserving Content During A Swap
If there is content that you wish to be preserved across swaps (e.g. a video player that you wish to remain playing
even if a swap occurs) you can use the [hx-preserve](@/attributes/hx-preserve.md)
attribute on the elements you wish to be preserved.
### Parameters
By default, an element that causes a request will include its value if it has one. If the element is a form it
will include the values of all inputs within it.
As with HTML forms, the `name` attribute of the input is used as the parameter name in the request that htmx sends.
Additionally, if the element causes a non-`GET` request, the values of all the inputs of the nearest enclosing form
will be included.
If you wish to include the values of other elements, you can use the [hx-include](@/attributes/hx-include.md) attribute
with a CSS selector of all the elements whose values you want to include in the request.
If you wish to filter out some parameters you can use the [hx-params](@/attributes/hx-params.md) attribute.
Finally, if you want to programmatically modify the parameters, you can use the [htmx:configRequest](@/events.md#htmx:configRequest)
event.
#### File Upload {#files}
If you wish to upload files via an htmx request, you can set the [hx-encoding](@/attributes/hx-encoding.md) attribute to
`multipart/form-data`. This will use a `FormData` object to submit the request, which will properly include the file
in the request.
Note that depending on your server-side technology, you may have to handle requests with this type of body content very
differently.
Note that htmx fires a `htmx:xhr:progress` event periodically based on the standard `progress` event during upload,
which you can hook into to show the progress of the upload.
See the [examples section](@/examples/_index.md) for more advanced form patterns, including [progress bars](@/examples/file-upload.md) and [error handling](@/examples/file-upload-input.md).
#### Extra Values
You can include extra values in a request using the [hx-vals](@/attributes/hx-vals.md) (name-expression pairs in JSON format) and
[hx-vars](@/attributes/hx-vars.md) attributes (comma-separated name-expression pairs that are dynamically computed).
### Confirming Requests {#confirming}
Often you will want to confirm an action before issuing a request. htmx supports the [`hx-confirm`](@/attributes/hx-confirm.md)
attribute, which allows you to confirm an action using a simple javascript dialog:
\`\`\`html
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
\`\`\`
Using events you can implement more sophisticated confirmation dialogs. The [confirm example](@/examples/confirm.md)
shows how to use [sweetalert2](https://sweetalert2.github.io/) library for confirmation of htmx actions.
#### Confirming Requests Using Events
Another option to do confirmation with is via the [`htmx:confirm` event](@/events.md#htmx:confirm). This event
is fired on *every* trigger for a request (not just on elements that have a `hx-confirm` attribute) and can be used
to implement asynchronous confirmation of the request.
Here is an example using [sweet alert](https://sweetalert.js.org/guides/) on any element with a `confirm-with-sweet-alert='true'` attribute on it:
\`\`\`javascript
document.body.addEventListener('htmx:confirm', function(evt) {
if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
evt.preventDefault();
swal({
title: "Are you sure?",
text: "Are you sure you are sure?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
evt.detail.issueRequest();
}
});
}
});
\`\`\`
## Attribute Inheritance {#inheritance}
Most attributes in htmx are inherited: they apply to the element they are on as well as any children elements. This
allows you to "hoist" attributes up the DOM to avoid code duplication. Consider the following htmx:
\`\`\`html
<button hx-delete="/account" hx-confirm="Are you sure?">
Delete My Account
</button>
<button hx-put="/account" hx-confirm="Are you sure?">
Update My Account
</button>
\`\`\`
Here we have a duplicate `hx-confirm` attribute. We can hoist this attribute to a parent element:
\`\`\`html
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
\`\`\`
This `hx-confirm` attribute will now apply to all htmx-powered elements within it.
Sometimes you wish to undo this inheritance. Consider if we had a cancel button to this group, but didn't want it to
be confirmed. We could add an `unset` directive on it like so:
\`\`\`html
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
<button hx-confirm="unset" hx-get="/">
Cancel
</button>
</div>
\`\`\`
The top two buttons would then show a confirm dialog, but the bottom cancel button would not.
Inheritance can be disabled on a per-element and per-attribute basis using the
[`hx-disinherit`](@/attributes/hx-disinherit.md) attribute.
If you wish to disable attribute inheritance entirely, you can set the `htmx.config.disableInheritance` configuration
variable to `true`. This will disable inheritance as a default, and allow you to specify inheritance explicitly
with the [`hx-inherit`](@/attributes/hx-inherit.md) attribute.
## Boosting
Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](@/attributes/hx-boost.md) attribute. This
attribute will convert all anchor tags and forms into AJAX requests that, by default, target the body of the page.
Here is an example:
\`\`\`html
<div hx-boost="true">
<a href="/blog">Blog</a>
</div>
\`\`\`
The anchor tag in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag.
### Progressive Enhancement {#progressive_enhancement}
A feature of `hx-boost` is that it degrades gracefully if javascript is not enabled: the links and forms continue
to work, they simply don't use ajax requests. This is known as
[Progressive Enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), and it allows
a wider audience to use your site's functionality.
Other htmx patterns can be adapted to achieve progressive enhancement as well, but they will require more thought.
Consider the [active search](@/examples/active-search.md) example. As it is written, it will not degrade gracefully:
someone who does not have javascript enabled will not be able to use this feature. This is done for simplicity’s sake,
to keep the example as brief as possible.
However, you could wrap the htmx-enhanced input in a form element:
\`\`\`html
<form action="/search" method="POST">
<input class="form-control" type="search"
name="search" placeholder="Begin typing to search users..."
hx-post="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
</form>
\`\`\`
With this in place, javascript-enabled clients would still get the nice active-search UX, but non-javascript enabled
clients would be able to hit the enter key and still search. Even better, you could add a "Search" button as well.
You would then need to update the form with an `hx-post` that mirrored the `action` attribute, or perhaps use `hx-boost`
on it.
You would need to check on the server side for the `HX-Request` header to differentiate between an htmx-driven and a
regular request, to determine exactly what to render to the client.
Other patterns can be adapted similarly to achieve the progressive enhancement needs of your application.
As you can see, this requires more thought and more work. It also rules some functionality entirely out of bounds.
These tradeoffs must be made by you, the developer, with respect to your projects goals and audience.
[Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) is a concept
closely related to progressive enhancement. Using progressive enhancement techniques such as `hx-boost` will make your
htmx application more accessible to a wide array of users.
htmx-based applications are very similar to normal, non-AJAX driven web applications because htmx is HTML-oriented.
As such, the normal HTML accessibility recommendations apply. For example:
* Use semantic HTML as much as possible (i.e. the right tags for the right things)
* Ensure focus state is clearly visible
* Associate text labels with all form fields
* Maximize the readability of your application with appropriate fonts, contrast, etc.
## Web Sockets & SSE {#websockets-and-sse}
Web Sockets and Server Sent Events (SSE) are supported via extensions. Please see
the [SSE extension](/extensions/sse) and [WebSocket extension](/extensions/ws)
pages to learn more.
## History Support {#history}
Htmx provides a simple mechanism for interacting with the [browser history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API):
If you want a given element to push its request URL into the browser navigation bar and add the current state of the page
to the browser's history, include the [hx-push-url](@/attributes/hx-push-url.md) attribute:
\`\`\`html
<a hx-get="/blog" hx-push-url="true">Blog</a>
\`\`\`
When a user clicks on this link, htmx will snapshot the current DOM and store it before it makes a request to /blog.
It then does the swap and pushes a new location onto the history stack.
When a user hits the back button, htmx will retrieve the old content from storage and swap it back into the target,
simulating "going back" to the previous state. If the location is not found in the cache, htmx will make an ajax
request to the given URL, with the header `HX-History-Restore-Request` set to true, and expects back the HTML needed
for the entire page. Alternatively, if the `htmx.config.refreshOnHistoryMiss` config variable is set to true, it will
issue a hard browser refresh.
**NOTE:** If you push a URL into the history, you **must** be able to navigate to that URL and get a full page back!
A user could copy and paste the URL into an email, or new tab. Additionally, htmx will need the entire page when restoring
history if the page is not in the history cache.
### Specifying History Snapshot Element
By default, htmx will use the `body` to take and restore the history snapshot from. This is usually the right thing, but
if you want to use a narrower element for snapshotting you can use the [hx-history-elt](@/attributes/hx-history-elt.md)
attribute to specify a different one.
Careful: this element will need to be on all pages or restoring from history won't work reliably.
### Undoing DOM Mutations By 3rd Party Libraries
If you are using a 3rd party library and want to use the htmx history feature, you will need to clean up the DOM before
a snapshot is taken. Let's consider the [Tom Select](https://tom-select.js.org/) library, which makes select elements
a much richer user experience. Let's set up TomSelect to turn any input element with the `.tomselect` class into a rich
select element.
First we need to initialize elements that have the class in new content:
\`\`\`javascript
htmx.onLoad(function (target) {
// find all elements in the new content that should be
// an editor and init w/ TomSelect
var editors = target.querySelectorAll(".tomselect")
.forEach(elt => new TomSelect(elt))
});
\`\`\`
This will create a rich selector for all input elements that have the `.tomselect` class on it. However, it mutates
the DOM and we don't want that mutation saved to the history cache, since TomSelect will be reinitialized when the
history content is loaded back into the screen.
To deal with this, we need to catch the `htmx:beforeHistorySave` event and clean out the TomSelect mutations by calling
`destroy()` on them:
\`\`\`javascript
htmx.on('htmx:beforeHistorySave', function() {
// find all TomSelect elements
document.querySelectorAll('.tomSelect')
.forEach(elt => elt.tomselect.destroy()) // and call destroy() on them
})
\`\`\`
This will revert the DOM to the original HTML, thus allowing for a clean snapshot.
### Disabling History Snapshots
History snapshotting can be disabled for a URL by setting the [hx-history](@/attributes/hx-history.md) attribute to `false`
on any element in the current document, or any html fragment loaded into the current document by htmx. This can be used
to prevent sensitive data entering the localStorage cache, which can be important for shared-use / public computers.
History navigation will work as expected, but on restoration the URL will be requested from the server instead of the
local history cache.
## Requests &amp; Responses {#requests}
Htmx expects responses to the AJAX requests it makes to be HTML, typically HTML fragments (although a full HTML
document, matched with a [hx-select](@/attributes/hx-select.md) tag can be useful too). Htmx will then swap the returned
HTML into the document at the target specified and with the swap strategy specified.
Sometimes you might want to do nothing in the swap, but still perhaps trigger a client side event ([see below](#response-headers)).
For this situation, by default, you can return a `204 - No Content` response code, and htmx will ignore the content of
the response.
In the event of an error response from the server (e.g. a 404 or a 501), htmx will trigger the [`htmx:responseError`](@/events.md#htmx:responseError)
event, which you can handle.
In the event of a connection error, the [`htmx:sendError`](@/events.md#htmx:sendError) event will be triggered.
### Configuring Response Handling {#response-handling}
You can configure the above behavior of htmx by mutating or replacing the `htmx.config.responseHandling` array. This
object is a collection of JavaScript objects defined like so:
\`\`\`js
responseHandling: [
{code:"204", swap: false}, // 204 - No Content by default does nothing, but is not an error
{code:"[23]..", swap: true}, // 200 & 300 responses are non-errors and are swapped
{code:"[45]..", swap: false, error:true}, // 400 & 500 responses are not swapped and are errors
{code:"...", swap: false} // catch all for any other response code
]
\`\`\`
When htmx receives a response it will iterate in order over the `htmx.config.responseHandling` array and test if the
`code` property of a given object, when treated as a Regular Expression, matches the current response. If an entry
does match the current response code, it will be used to determine if and how the response will be processed.
The fields available for response handling configuration on entries in this array are:
* `code` - a String representing a regular expression that will be tested against response codes.
* `swap` - `true` if the response should be swapped into the DOM, `false` otherwise
* `error` - `true` if htmx should treat this response as an error
* `ignoreTitle` - `true` if htmx should ignore title tags in the response
* `select` - A CSS selector to use to select content from the response
* `target` - A CSS selector specifying an alternative target for the response
* `swapOverride` - An alternative swap mechanism for the response
#### Configuring Response Handling Examples {#response-handling-examples}
As an example of how to use this configuration, consider a situation when a server-side framework responds with a
[`422 - Unprocessable Entity`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) response when validation errors occur. By default, htmx will ignore the response,
since it matches the Regular Expression `[45]..`.
Using the [meta config](#configuration-options) mechanism for configuring responseHandling, we could add the following
config:
\`\`\`html
<!--
* 204 No Content by default does nothing, but is not an error
* 2xx, 3xx and 422 responses are non-errors and are swapped
* 4xx & 5xx responses are not swapped and are errors
* all other responses are swapped using "..." as a catch-all
-->
<meta
name="htmx-config"
content='{
"responseHandling":[
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": true}
]
}'
/>
\`\`\`
If you wanted to swap everything, regardless of HTTP response code, you could use this configuration:
\`\`\`html
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!--all responses are swapped-->
\`\`\`
Finally, it is worth considering using the [Response Targets](/extensions/response-targets)
extension, which allows you to configure the behavior of response codes declaratively via attributes.
### CORS
When using htmx in a cross origin context, remember to configure your web
server to set Access-Control headers in order for htmx headers to be visible
on the client side.
- [Access-Control-Allow-Headers (for request headers)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers)
- [Access-Control-Expose-Headers (for response headers)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)
[See all the request and response headers that htmx implements.](@/reference.md#request_headers)
### Request Headers
htmx includes a number of useful headers in requests:
| Header | Description |
|--------|-------------|
| `HX-Boosted` | indicates that the request is via an element using [hx-boost](@/attributes/hx-boost.md)
| `HX-Current-URL` | the current URL of the browser
| `HX-History-Restore-Request` | "true" if the request is for history restoration after a miss in the local history cache
| `HX-Prompt` | the user response to an [hx-prompt](@/attributes/hx-prompt.md)
| `HX-Request` | always "true"
| `HX-Target` | the `id` of the target element if it exists
| `HX-Trigger-Name` | the `name` of the triggered element if it exists
| `HX-Trigger` | the `id` of the triggered element if it exists
### Response Headers
htmx supports some htmx-specific response headers:
* [`HX-Location`](@/headers/hx-location.md) - allows you to do a client-side redirect that does not do a full page reload
* [`HX-Push-Url`](@/headers/hx-push-url.md) - pushes a new url into the history stack
* [`HX-Redirect`](@/headers/hx-redirect.md) - can be used to do a client-side redirect to a new location
* `HX-Refresh` - if set to "true" the client-side will do a full refresh of the page
* [`HX-Replace-Url`](@/headers/hx-replace-url.md) - replaces the current URL in the location bar
* `HX-Reswap` - allows you to specify how the response will be swapped. See [hx-swap](@/attributes/hx-swap.md) for possible values
* `HX-Retarget` - a CSS selector that updates the target of the content update to a different element on the page
* `HX-Reselect` - a CSS selector that allows you to choose which part of the response is used to be swapped in. Overrides an existing [`hx-select`](@/attributes/hx-select.md) on the triggering element
* [`HX-Trigger`](@/headers/hx-trigger.md) - allows you to trigger client-side events
* [`HX-Trigger-After-Settle`](@/headers/hx-trigger.md) - allows you to trigger client-side events after the settle step
* [`HX-Trigger-After-Swap`](@/headers/hx-trigger.md) - allows you to trigger client-side events after the swap step
For more on the `HX-Trigger` headers, see [`HX-Trigger` Response Headers](@/headers/hx-trigger.md).
Submitting a form via htmx has the benefit of no longer needing the [Post/Redirect/Get Pattern](https://en.wikipedia.org/wiki/Post/Redirect/Get).
After successfully processing a POST request on the server, you don't need to return a [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). You can directly return the new HTML fragment.
Also the response headers above are not provided to htmx for processing with 3xx Redirect response codes like [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). Instead, the browser will intercept the redirection internally and return the headers and response from the redirected URL. Where possible use alternative response codes like 200 to allow returning of these response headers.
### Request Order of Operations {#request-operations}
The order of operations in a htmx request are:
* The element is triggered and begins a request
* Values are gathered for the request
* The `htmx-request` class is applied to the appropriate elements
* The request is then issued asynchronously via AJAX
* Upon getting a response the target element is marked with the `htmx-swapping` class
* An optional swap delay is applied (see the [hx-swap](@/attributes/hx-swap.md) attribute)
* The actual content swap is done
* the `htmx-swapping` class is removed from the target
* the `htmx-added` class is added to each new piece of content
* the `htmx-settling` class is applied to the target
* A settle delay is done (default: 20ms)
* The DOM is settled
* the `htmx-settling` class is removed from the target
* the `htmx-added` class is removed from each new piece of content
You can use the `htmx-swapping` and `htmx-settling` classes to create
[CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) between pages.
## Validation
Htmx integrates with the [HTML5 Validation API](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation)
and will not issue a request for a form if a validatable input is invalid. This is true for both AJAX requests as well as
WebSocket sends.
Htmx fires events around validation that can be used to hook in custom validation and error handling:
* `htmx:validation:validate` - called before an element's `checkValidity()` method is called. May be used to add in
custom validation logic
* `htmx:validation:failed` - called when `checkValidity()` returns false, indicating an invalid input
* `htmx:validation:halted` - called when a request is not issued due to validation errors. Specific errors may be found
in the `event.detail.errors` object
Non-form elements do not validate before they make requests by default, but you can enable validation by setting
the [`hx-validate`](@/attributes/hx-validate.md) attribute to "true".
### Validation Example
Here is an example of an input that uses the [`hx-on`](/attributes/hx-on) attribute to catch the
`htmx:validation:validate` event and require that the input have the value `foo`:
\`\`\`html
<form id="example-form" hx-post="/test">
<input name="example"
onkeyup="this.setCustomValidity('') // reset the validation on keyup"
hx-on:htmx:validation:validate="if(this.value != 'foo') {
this.setCustomValidity('Please enter the value foo') // set the validation error
htmx.find('#example-form').reportValidity() // report the issue
}">
</form>
\`\`\`
Note that all client side validations must be re-done on the server side, as they can always be bypassed.
## Animations
Htmx allows you to use [CSS transitions](#css_transitions)
in many situations using only HTML and CSS.
Please see the [Animation Guide](@/examples/animations.md) for more details on the options available.
## Extensions
htmx provides an [extensions](/extensions) mechanism that allows you to customize the libraries' behavior.
Extensions [are defined in javascript](/extensions/building) and then enabled via
the [`hx-ext`](@/attributes/hx-ext.md) attribute.
### Core Extensions
htmx supports a few "core" extensions, which are supported by the htmx development team:
* [head-support](/extensions/head-support) - support for merging head tag information (styles, etc.) in htmx requests
* [htmx-1-compat](/extensions/htmx-1-compat) - restores htmx 1 defaults & functionality
* [idiomorph](/extensions/idiomorph) - supports the `morph` swap strategy using idiomorph
* [preload](/extensions/preload) - allows you to preload content for better performance
* [response-targets](/extensions/response-targets) - allows you to target elements based on HTTP response codes (e.g. `404`)
* [sse](/extensions/sse) - support for [Server Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
* [ws](/extensions/ws) - support for [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
You can see all available extensions on the [Extensions](/extensions) page.
### Installing Extensions
The fastest way to install htmx extensions created by others is to load them via a CDN. Remember to always include the core htmx library before the extensions and [enable the extension](#enabling-extensions). For example, if you would like to use the [response-targets](/extensions/response-targets) extension, you can add this to your head tag:
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-response-targets@2.0.2" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous"></script>
</head>
<body hx-ext="extension-name">
...
\`\`\`
An unminified version is also available at `https://unpkg.com/htmx-ext-extension-name/dist/extension-name.js` (replace `extension-name` with the name of the extension).
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install htmx extensions is to simply copy them into your project. Download the extension from `https://unpkg.com/htmx-ext-extension-name` (replace `extension-name` with the name of the extension) e.g., https://unpkg.com/htmx-ext-response-targets. Then add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install htmx extensions via [npm](https://www.npmjs.com/) (replace `extension-name` with the name of the extension):
\`\`\`shell
npm install htmx-ext-extension-name
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-extension-name/dist/extension-name.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-extension-name` via npm (replace `extension-name` with the name of the extension)
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-extension-name`; // replace `extension-name` with the name of the extension
\`\`\`
Note: [Idiomorph](/extensions/idiomorph) does not follow the naming convention of htmx extensions. Use `idiomorph` instead of `htmx-ext-idiomorph`. For example, `https://unpkg.com/idiomorph` or `npm install idiomorph`.
Note: Community extensions hosted outside this repository might have different installation instructions. Please check the corresponding repository for set-up guidance.
### Enabling Extensions
To enable an extension, add a `hx-ext="extension-name"` attribute to `<body>` or another HTML element (replace `extension-name` with the name of the extension). The extension will be applied to all child elements.
The following example shows how to enable [response-targets](/extensions/response-targets) extension, allowing you to specify different target elements to be swapped based on HTTP response code.
\`\`\`html
<body hx-ext="response-targets">
...
<button hx-post="/register" hx-target="#response-div" hx-target-404="#not-found">
Register!
</button>
<div id="response-div"></div>
<div id="not-found"></div>
...
</body>
\`\`\`
### Creating Extensions
If you are interested in adding your own extension to htmx, please [see the extension docs](/extensions/building).
## Events & Logging {#events}
Htmx has an extensive [events mechanism](@/reference.md#events), which doubles as the logging system.
If you want to register for a given htmx event you can use
\`\`\`js
document.body.addEventListener('htmx:load', function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
\`\`\`
or, if you would prefer, you can use the following htmx helper:
\`\`\`javascript
htmx.on("htmx:load", function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
\`\`\`
The `htmx:load` event is fired every time an element is loaded into the DOM by htmx, and is effectively the equivalent
to the normal `load` event.
Some common uses for htmx events are:
### Initialize A 3rd Party Library With Events {#init_3rd_party_with_events}
Using the `htmx:load` event to initialize content is so common that htmx provides a helper function:
\`\`\`javascript
htmx.onLoad(function(target) {
myJavascriptLib.init(target);
});
\`\`\`
This does the same thing as the first example, but is a little cleaner.
### Configure a Request With Events {#config_request_with_events}
You can handle the [`htmx:configRequest`](@/events.md#htmx:configRequest) event in order to modify an AJAX request before it is issued:
\`\`\`javascript
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the request
evt.detail.headers['Authentication-Token'] = getAuthToken(); // add a new header into the request
});
\`\`\`
Here we add a parameter and header to the request before it is sent.
### Modifying Swapping Behavior With Events {#modifying_swapping_behavior_with_events}
You can handle the [`htmx:beforeSwap`](@/events.md#htmx:beforeSwap) event in order to modify the swap behavior of htmx:
\`\`\`javascript
document.body.addEventListener('htmx:beforeSwap', function(evt) {
if(evt.detail.xhr.status === 404){
// alert the user when a 404 occurs (maybe use a nicer mechanism than alert())
alert("Error: Could Not Find Resource");
} else if(evt.detail.xhr.status === 422){
// allow 422 responses to swap as we are using this as a signal that
// a form was submitted with bad data and want to rerender with the
// errors
//
// set isError to false to avoid error logging in console
evt.detail.shouldSwap = true;
evt.detail.isError = false;
} else if(evt.detail.xhr.status === 418){
// if the response code 418 (I'm a teapot) is returned, retarget the
// content of the response to the element with the id `teapot`
evt.detail.shouldSwap = true;
evt.detail.target = htmx.find("#teapot");
}
});
\`\`\`
Here we handle a few [400-level error response codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses)
that would normally not do a swap in htmx.
### Event Naming {#event_naming}
Note that all events are fired with two different names
* Camel Case
* Kebab Case
So, for example, you can listen for `htmx:afterSwap` or for `htmx:after-swap`. This facilitates interoperability
with other libraries. [Alpine.js](https://github.com/alpinejs/alpine/), for example, requires kebab case.
### Logging
If you set a logger at `htmx.logger`, every event will be logged. This can be very useful for troubleshooting:
\`\`\`javascript
htmx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
\`\`\`
## Debugging
Declarative and event driven programming with htmx (or any other declarative language) can be a wonderful and highly productive
activity, but one disadvantage when compared with imperative approaches is that it can be trickier to debug.
Figuring out why something *isn't* happening, for example, can be difficult if you don't know the tricks.
Well, here are the tricks:
The first debugging tool you can use is the `htmx.logAll()` method. This will log every event that htmx triggers and
will allow you to see exactly what the library is doing.
\`\`\`javascript
htmx.logAll();
\`\`\`
Of course, that won't tell you why htmx *isn't* doing something. You might also not know *what* events a DOM
element is firing to use as a trigger. To address this, you can use the
[`monitorEvents()`](https://developers.google.com/web/updates/2015/05/quickly-monitor-events-from-the-console-panel) method available in the
browser console:
\`\`\`javascript
monitorEvents(htmx.find("#theElement"));
\`\`\`
This will spit out all events that are occurring on the element with the id `theElement` to the console, and allow you
to see exactly what is going on with it.
Note that this *only* works from the console, you cannot embed it in a script tag on your page.
Finally, push come shove, you might want to just debug `htmx.js` by loading up the unminimized version. It's
about 2500 lines of javascript, so not an insurmountable amount of code. You would most likely want to set a break
point in the `issueAjaxRequest()` and `handleAjaxResponse()` methods to see what's going on.
And always feel free to jump on the [Discord](https://htmx.org/discord) if you need help.
### Creating Demos
Sometimes, in order to demonstrate a bug or clarify a usage, it is nice to be able to use a javascript snippet
site like [jsfiddle](https://jsfiddle.net/). To facilitate easy demo creation, htmx hosts a demo script
site that will install:
* htmx
* hyperscript
* a request mocking library
Simply add the following script tag to your demo/fiddle/whatever:
\`\`\`html
<script src="https://demo.htmx.org"></script>
\`\`\`
This helper allows you to add mock responses by adding `template` tags with a `url` attribute to indicate which URL.
The response for that url will be the innerHTML of the template, making it easy to construct mock responses. You can
add a delay to the response with a `delay` attribute, which should be an integer indicating the number of milliseconds
to delay
You may embed simple expressions in the template with the `${}` syntax.
Note that this should only be used for demos and is in no way guaranteed to work for long periods of time
as it will always be grabbing the latest versions htmx and hyperscript!
#### Demo Example
Here is an example of the code in action:
\`\`\`html
<!-- load demo environment -->
<script src="https://demo.htmx.org"></script>
<!-- post to /foo -->
<button hx-post="/foo" hx-target="#result">
Count Up
</button>
<output id="result"></output>
<!-- respond to /foo with some dynamic content in a template tag -->
<script>
globalInt = 0;
</script>
<template url="/foo" delay="500"> <!-- note the url and delay attributes -->
${globalInt++}
</template>
\`\`\`
## Scripting {#scripting}
While htmx encourages a hypermedia approach to building web applications, it offers many options for client scripting. Scripting is included in the REST-ful description of web architecture, see: [Code-On-Demand](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_7). As much as is feasible, we recommend a [hypermedia-friendly](/essays/hypermedia-friendly-scripting) approach to scripting in your web application:
* [Respect HATEOAS](/essays/hypermedia-friendly-scripting#prime_directive)
* [Use events to communicate between components](/essays/hypermedia-friendly-scripting#events)
* [Use islands to isolate non-hypermedia components from the rest of your application](/essays/hypermedia-friendly-scripting#islands)
* [Consider inline scripting](/essays/hypermedia-friendly-scripting#inline)
The primary integration point between htmx and scripting solutions is the [events](#events) that htmx sends and can
respond to. See the SortableJS example in the [3rd Party Javascript](#3rd-party) section for a good template for
integrating a JavaScript library with htmx via events.
Scripting solutions that pair well with htmx include:
* [VanillaJS](http://vanilla-js.com/) - Simply using the built-in abilities of JavaScript to hook in event handlers to
respond to the events htmx emits can work very well for scripting. This is an extremely lightweight and increasingly
popular approach.
* [AlpineJS](https://alpinejs.dev/) - Alpine.js provides a rich set of tools for creating sophisticated front end scripts,
including reactive programming support, while still remaining extremely lightweight. Alpine encourages the "inline scripting"
approach that we feel pairs well with htmx.
* [jQuery](https://jquery.com/) - Despite its age and reputation in some circles, jQuery pairs very well with htmx, particularly
in older code-bases that already have a lot of jQuery in them.
* [hyperscript](https://hyperscript.org) - Hyperscript is an experimental front-end scripting language created by the same
team that created htmx. It is designed to embed well in HTML and both respond to and create events, and pairs very well
with htmx.
We have an entire chapter entitled ["Client-Side Scripting"](https://hypermedia.systems/client-side-scripting/) in [our
book](https://hypermedia.systems) that looks at how scripting can be integrated into your htmx-based application.
### <a name="hx-on"></a>[The `hx-on*` Attributes](#hx-on)
HTML allows the embedding of inline scripts via the [`onevent` properties](https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers#using_onevent_properties),
such as `onClick`:
\`\`\`html
<button onclick="alert('You clicked me!')">
Click Me!
</button>
\`\`\`
This feature allows scripting logic to be co-located with the HTML elements the logic applies to, giving good
[Locality of Behaviour (LoB)](/essays/locality-of-behaviour). Unfortunately, HTML only allows `on*` attributes for a fixed
number of [specific DOM events](https://www.w3schools.com/tags/ref_eventattributes.asp) (e.g. `onclick`) and
doesn't provide a generalized mechanism for responding to arbitrary events on elements.
In order to address this shortcoming, htmx offers [`hx-on*`](/attributes/hx-on) attributes. These attributes allow
you to respond to any event in a manner that preserves the LoB of the standard `on*` properties.
If we wanted to respond to the `click` event using an `hx-on` attribute, we would write this:
\`\`\`html
<button hx-on:click="alert('You clicked me!')">
Click Me!
</button>
\`\`\`
So, the string `hx-on`, followed by a colon (or a dash), then by the name of the event.
For a `click` event, of course, we would recommend sticking with the standard `onclick` attribute. However, consider an
htmx-powered button that wishes to add a parameter to a request using the `htmx:config-request` event. This would not
be possible using a standard `on*` property, but it can be done using the `hx-on:htmx:config-request` attribute:
\`\`\`html
<button hx-post="/example"
hx-on:htmx:config-request="event.detail.parameters.example = 'Hello Scripting!'">
Post Me!
</button>
\`\`\`
Here the `example` parameter is added to the `POST` request before it is issued, with the value 'Hello Scripting!'.
Another usecase is to [reset user input](@/examples/reset-user-input.md) on successful requests using the `afterRequest`
event, avoiding the need for something like an out of band swap.
The `hx-on*` attributes are a very simple mechanism for generalized embedded scripting. It is _not_ a replacement for more
fully developed front-end scripting solutions such as AlpineJS or hyperscript. It can, however, augment a VanillaJS-based
approach to scripting in your htmx-powered application.
Note that HTML attributes are *case insensitive*. This means that, unfortunately, events that rely on capitalization/
camel casing, cannot be responded to. If you need to support camel case events we recommend using a more fully
functional scripting solution such as AlpineJS or hyperscript. htmx dispatches all its events in both camelCase and in
kebab-case for this very reason.
### 3rd Party Javascript {#3rd-party}
Htmx integrates fairly well with third party libraries. If the library fires events on the DOM, you can use those events to
trigger requests from htmx.
A good example of this is the [SortableJS demo](@/examples/sortable.md):
\`\`\`html
<form class="sortable" hx-post="/items" hx-trigger="end">
<div class="htmx-indicator">Updating...</div>
<div><input type='hidden' name='item' value='1'/>Item 1</div>
<div><input type='hidden' name='item' value='2'/>Item 2</div>
<div><input type='hidden' name='item' value='2'/>Item 3</div>
</form>
\`\`\`
With Sortable, as with most javascript libraries, you need to initialize content at some point.
In jquery you might do this like so:
\`\`\`javascript
$(document).ready(function() {
var sortables = document.body.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
});
\`\`\`
In htmx, you would instead use the `htmx.onLoad` function, and you would select only from the newly loaded content,
rather than the entire document:
\`\`\`js
htmx.onLoad(function(content) {
var sortables = content.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
})
\`\`\`
This will ensure that as new content is added to the DOM by htmx, sortable elements are properly initialized.
If javascript adds content to the DOM that has htmx attributes on it, you need to make sure that this content
is initialized with the `htmx.process()` function.
For example, if you were to fetch some data and put it into a div using the `fetch` API, and that HTML had
htmx attributes in it, you would need to add a call to `htmx.process()` like this:
\`\`\`js
let myDiv = document.getElementById('my-div')
fetch('http://example.com/movies.json')
.then(response => response.text())
.then(data => { myDiv.innerHTML = data; htmx.process(myDiv); } );
\`\`\`
Some 3rd party libraries create content from HTML template elements. For instance, Alpine JS uses the `x-if`
attribute on templates to add content conditionally. Such templates are not initially part of the DOM and,
if they contain htmx attributes, will need a call to `htmx.process()` after they are loaded. The following
example uses Alpine's `$watch` function to look for a change of value that would trigger conditional content:
\`\`\`html
<div x-data="{show_new: false}"
x-init="$watch('show_new', value => {
if (show_new) {
htmx.process(document.querySelector('#new_content'))
}
})">
<button @click = "show_new = !show_new">Toggle New Content</button>
<template x-if="show_new">
<div id="new_content">
<a hx-get="/server/newstuff" href="#">New Clickable</a>
</div>
</template>
</div>
\`\`\`
#### Web Components {#web-components}
Please see the [Web Components Examples](@/examples/web-components.md) page for examples on how to integrate htmx
with web components.
## Caching
htmx works with standard [HTTP caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching)
mechanisms out of the box.
If your server adds the
[`Last-Modified`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified)
HTTP response header to the response for a given URL, the browser will automatically add the
[`If-Modified-Since`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since)
request HTTP header to the next requests to the same URL. Be mindful that if
your server can render different content for the same URL depending on some other
headers, you need to use the [`Vary`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#vary)
response HTTP header. For example, if your server renders the full HTML when the
`HX-Request` header is missing or `false`, and it renders a fragment of that HTML
when `HX-Request: true`, you need to add `Vary: HX-Request`. That causes the cache to be
keyed based on a composite of the response URL and the `HX-Request` request header —
rather than being based just on the response URL.
If you are unable (or unwilling) to use the `Vary` header, you can alternatively set the configuration parameter
`getCacheBusterParam` to `true`. If this configuration variable is set, htmx will include a cache-busting parameter
in `GET` requests that it makes, which will prevent browsers from caching htmx-based and non-htmx based responses
in the same cache slot.
htmx also works with [`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag)
as expected. Be mindful that if your server can render different content for the same
URL (for example, depending on the value of the `HX-Request` header), the server needs
to generate a different `ETag` for each content.
## Security
htmx allows you to define logic directly in your DOM. This has a number of advantages, the largest being
[Locality of Behavior](@/essays/locality-of-behaviour.md), which makes your system easier to understand and
maintain.
A concern with this approach, however, is security: since htmx increases the expressiveness of HTML, if a malicious
user is able to inject HTML into your application, they can leverage this expressiveness of htmx to malicious
ends.
### Rule 1: Escape All User Content
The first rule of HTML-based web development has always been: *do not trust input from the user*. You should escape all
3rd party, untrusted content that is injected into your site. This is to prevent, among other issues,
[XSS attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
There is extensive documentation on XSS and how to prevent it on the excellent [OWASP Website](https://owasp.org/www-community/attacks/xss/),
including a [Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).
The good news is that this is a very old and well understood topic, and the vast majority of server-side templating languages
support [automatic escaping](https://docs.djangoproject.com/en/4.2/ref/templates/language/#automatic-html-escaping) of
content to prevent just such an issue.
That being said, there are times people choose to inject HTML more dangerously, often via some sort of `raw()`
mechanism in their templating language. This can be done for good reasons, but if the content being injected is coming
from a 3rd party then it _must_ be scrubbed, including removing attributes starting with `hx-` and `data-hx`, as well as
inline `<script>` tags, etc.
If you are injecting raw HTML and doing your own escaping, a best practice is to *whitelist* the attributes and tags you
allow, rather than to blacklist the ones you disallow.
### htmx Security Tools
Of course, bugs happen and developers are not perfect, so it is good to have a layered approach to security for
your web application, and htmx provides tools to help secure your application as well.
Let's take a look at them.
#### `hx-disable`
The first tool htmx provides to help further secure your application is the [`hx-disable`](/attributes/hx-disable)
attribute. This attribute will prevent processing of all htmx attributes on a given element, and on all elements within
it. So, for example, if you were including raw HTML content in a template (again, this is not recommended!) then you
could place a div around the content with the `hx-disable` attribute on it:
\`\`\`html
<div hx-disable>
<%= raw(user_content) %>
</div>
\`\`\`
And htmx will not process any htmx-related attributes or features found in that content. This attribute cannot be
disabled by injecting further content: if an `hx-disable` attribute is found anywhere in the parent hierarchy of an
element, it will not be processed by htmx.
#### `hx-history`
Another security consideration is htmx history cache. You may have pages that have sensitive data that you do not
want stored in the users `localStorage` cache. You can omit a given page from the history cache by including the
[`hx-history`](/attributes/hx-history) attribute anywhere on the page, and setting its value to `false`.
#### Configuration Options
htmx also provides configuration options related to security:
* `htmx.config.selfRequestsOnly` - if set to `true`, only requests to the same domain as the current document will be allowed
* `htmx.config.allowScriptTags` - htmx will process `<script>` tags found in new content it loads. If you wish to disable
this behavior you can set this configuration variable to `false`
* `htmx.config.historyCacheSize` - can be set to `0` to avoid storing any HTML in the `localStorage` cache
* `htmx.config.allowEval` - can be set to `false` to disable all features of htmx that rely on eval:
* event filters
* `hx-on:` attributes
* `hx-vals` with the `js:` prefix
* `hx-headers` with the `js:` prefix
Note that all features removed by disabling `eval()` can be reimplemented using your own custom javascript and the
htmx event model.
#### Events
If you want to allow requests to some domains beyond the current host, but not leave things totally open, you can
use the `htmx:validateUrl` event. This event will have the request URL available in the `detail.url` slot, as well
as a `sameHost` property.
You can inspect these values and, if the request is not valid, invoke `preventDefault()` on the event to prevent the
request from being issued.
\`\`\`javascript
document.body.addEventListener('htmx:validateUrl', function (evt) {
// only allow requests to the current server as well as myserver.com
if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
evt.preventDefault();
}
});
\`\`\`
### CSP Options
Browsers also provide tools for further securing your web application. The most powerful tool available is a
[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). Using a CSP you can tell the
browser to, for example, not issue requests to non-origin hosts, to not evaluate inline script tags, etc.
Here is an example CSP in a `meta` tag:
\`\`\`html
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
\`\`\`
This tells the browser "Only allow connections to the original (source) domain". This would be redundant with the
`htmx.config.selfRequestsOnly`, but a layered approach to security is warranted and, in fact, ideal, when dealing
with application security.
A full discussion of CSPs is beyond the scope of this document, but the [MDN Article](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) provide a good jumping off point
for exploring this topic.
### CSRF Prevention
The assignment and checking of CSRF tokens are typically backend responsibilities, but `htmx` can support returning the CSRF token automatically with every request using the `hx-headers` attribute. The attribute needs to be added to the element issuing the request or one of its ancestor elements. This makes the `html` and `body` elements effective global vehicles for adding the CSRF token to the `HTTP` request header, as illustrated below.
\`\`\`html
<html lang="en" hx-headers='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
:
</html>
\`\`\`
\`\`\`html
<body hx-headers='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
:
</body>
\`\`\`
The above elements are usually unique in an HTML document and should be easy to locate within templates.
## Configuring htmx {#config}
Htmx has some configuration options that can be accessed either programmatically or declaratively. They are
listed below:
<div class="info-table">
| Config Variable | Info |
|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `htmx.config.historyEnabled` | defaults to `true`, really only useful for testing |
| `htmx.config.historyCacheSize` | defaults to 10 |
| `htmx.config.refreshOnHistoryMiss` | defaults to `false`, if set to `true` htmx will issue a full page refresh on history misses rather than use an AJAX request |
| `htmx.config.defaultSwapStyle` | defaults to `innerHTML` |
| `htmx.config.defaultSwapDelay` | defaults to 0 |
| `htmx.config.defaultSettleDelay` | defaults to 20 |
| `htmx.config.includeIndicatorStyles` | defaults to `true` (determines if the indicator styles are loaded) |
| `htmx.config.indicatorClass` | defaults to `htmx-indicator` |
| `htmx.config.requestClass` | defaults to `htmx-request` |
| `htmx.config.addedClass` | defaults to `htmx-added` |
| `htmx.config.settlingClass` | defaults to `htmx-settling` |
| `htmx.config.swappingClass` | defaults to `htmx-swapping` |
| `htmx.config.allowEval` | defaults to `true`, can be used to disable htmx's use of eval for certain features (e.g. trigger filters) |
| `htmx.config.allowScriptTags` | defaults to `true`, determines if htmx will process script tags found in new content |
| `htmx.config.inlineScriptNonce` | defaults to `''`, meaning that no nonce will be added to inline scripts |
| `htmx.config.attributesToSettle` | defaults to `["class", "style", "width", "height"]`, the attributes to settle during the settling phase |
| `htmx.config.inlineStyleNonce` | defaults to `''`, meaning that no nonce will be added to inline styles |
| `htmx.config.useTemplateFragments` | defaults to `false`, HTML template tags for parsing content from the server (not IE11 compatible!) |
| `htmx.config.wsReconnectDelay` | defaults to `full-jitter` |
| `htmx.config.wsBinaryType` | defaults to `blob`, the [type of binary data](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) being received over the WebSocket connection |
| `htmx.config.disableSelector` | defaults to `[hx-disable], [data-hx-disable]`, htmx will not process elements with this attribute on it or a parent |
| `htmx.config.withCredentials` | defaults to `false`, allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates |
| `htmx.config.timeout` | defaults to 0, the number of milliseconds a request can take before automatically being terminated |
| `htmx.config.scrollBehavior` | defaults to 'instant', the scroll behavior when using the [show](@/attributes/hx-swap.md#scrolling-scroll-show) modifier with `hx-swap`. The allowed values are `instant` (scrolling should happen instantly in a single jump), `smooth` (scrolling should animate smoothly) and `auto` (scroll behavior is determined by the computed value of [scroll-behavior](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior)). |
| `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier. |
| `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will append the target element to the `GET` request in the format `org.htmx.cache-buster=targetElementId` |
| `htmx.config.globalViewTransitions` | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content. |
| `htmx.config.methodsThatUseUrlParams` | defaults to `["get", "delete"]`, htmx will format requests with these methods by encoding their parameters in the URL, not the request body |
| `htmx.config.selfRequestsOnly` | defaults to `true`, whether to only allow AJAX requests to the same domain as the current document |
| `htmx.config.ignoreTitle` | defaults to `false`, if set to `true` htmx will not update the title of the document when a `title` tag is found in new content |
| `htmx.config.disableInheritance` | disables attribute inheritance in htmx, which can then be overridden by the [`hx-inherit`](@/attributes/hx-inherit.md) attribute |
| `htmx.config.scrollIntoViewOnBoost` | defaults to `true`, whether or not the target of a boosted element is scrolled into the viewport. If `hx-target` is omitted on a boosted element, the target defaults to `body`, causing the page to scroll to the top. |
| `htmx.config.triggerSpecsCache` | defaults to `null`, the cache to store evaluated trigger specifications into, improving parsing performance at the cost of more memory usage. You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) |
| `htmx.config.responseHandling` | the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error |
| `htmx.config.allowNestedOobSwaps` | defaults to `true`, whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps). |
</div>
You can set them directly in javascript, or you can use a `meta` tag:
\`\`\`html
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
\`\`\`
## Conclusion
And that's it!
Have fun with htmx! You can accomplish [quite a bit](@/examples/_index.md) without writing a lot of code!
</div>
</div>
```
# events.md
```md
+++
title = "Events"
+++
Htmx provides an extensive events system that can be used to modify and enhance behavior. Events
are listed below.
### Event - `htmx:abort` {#htmx:abort}
This event is different than other events: htmx does not *trigger* it, but rather *listens* for it.
If you send an `htmx:abort` event to an element making a request, it will abort the request:
\`\`\`html
<button id="request-button" hx-post="/example">Issue Request</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">Cancel Request</button>
\`\`\`
### Event - `htmx:afterOnLoad` {#htmx:afterOnLoad}
This event is triggered after an AJAX `onload` has finished. Note that this does not mean that the content
has been swapped or settled yet, only that the request has finished.
##### Details
* `detail.elt` - the element that dispatched the request or if the body no longer contains the element then the closest parent
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:afterProcessNode` {#htmx:afterProcessNode}
This event is triggered after htmx has initialized a DOM node. It can be useful for extensions to build additional features onto a node.
##### Details
* `detail.elt` - the element being initialized
### Event - `htmx:afterRequest` {#htmx:afterRequest}
This event is triggered after an AJAX request has finished either in the case of a successful request (although
one that may have returned a remote error code such as a `404`) or in a network error situation. This event
can be paired with [`htmx:beforeRequest`](#htmx:beforeRequest) to wrap behavior around a request cycle.
##### Details
* `detail.elt` - the element that dispatched the request or if the body no longer contains the element then the closest parent
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
* `detail.successful` - true if the response has a 20x status code or is marked `detail.isError = false` in the
`htmx:beforeSwap` event, else false
* `detail.failed` - true if the response does not have a 20x status code or is marked `detail.isError = true` in the
`htmx:beforeSwap` event, else false
### Event - `htmx:afterSettle` {#htmx:afterSettle}
This event is triggered after the DOM has [settled](@/docs.md#request-operations).
##### Details
* `detail.elt` - the updated element
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:afterSwap` {#htmx:afterSwap}
This event is triggered after new content has been [swapped into the DOM](@/docs.md#swapping).
##### Details
* `detail.elt` - the swapped in element
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeCleanupElement` {#htmx:beforeCleanupElement}
This event is triggered before htmx [disables](@/attributes/hx-disable.md) an element or removes it from the DOM.
##### Details
* `detail.elt` - the element to be cleaned up
### Event - `htmx:beforeOnLoad` {#htmx:beforeOnLoad}
This event is triggered before any response processing occurs. If you call `preventDefault()` on the event to cancel it, no swap will occur.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeProcessNode` {#htmx:beforeProcessNode}
This event is triggered before htmx initializes a DOM node and has processed all of its `hx-` attributes. This gives extensions and other external code the ability to modify the contents of a DOM node before it is processed.
##### Details
* `detail.elt` - the element being initialized
### Event - `htmx:beforeRequest` {#htmx:beforeRequest}
This event is triggered before an AJAX request is issued. If you call `preventDefault()` on the event to cancel it, no request will occur.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.boosted` - true if the request is via an element using boosting
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeSend` {#htmx:beforeSend}
This event is triggered right before a request is sent. You may not cancel the request with this event.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:beforeSwap` {#htmx:beforeSwap}
This event is triggered before any new content has been [swapped into the DOM](@/docs.md#swapping).
Most values on `detail` can be set to override subsequent behavior, other than where response headers take precedence.
If you call `preventDefault()` on the event to cancel it, no swap will occur.
You can modify the default swap behavior by modifying the `shouldSwap`, `selectOverride`, `swapOverride` and `target` properties of the event detail.
See the documentation on [configuring swapping](@/docs.md#modifying_swapping_behavior_with_events) for more details.
##### Details
* `detail.elt` - the target of the swap
* `detail.xhr` - the `XMLHttpRequest`
* `detail.boosted` - true if the request is via an element using boosting
* `detail.requestConfig` - the configuration of the AJAX request
* `detail.requestConfig.elt` - the element that dispatched the request
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
* `detail.ignoreTitle` - if `true` any title tag in the response will be ignored
* `detail.isError` - whether error events should be triggered and also determines the values of `detail.successful` and `detail.failed` in later events
* `detail.serverResponse` - the server response as text to be used for the swap
* `detail.selectOverride` - add this to use instead of an [`hx-select`](@/attributes/hx-select.md) value
* `detail.swapOverride` - add this to use instead of an [`hx-swap`](@/attributes/hx-swap.md) value
* `detail.target` - the target of the swap
### Event - `htmx:beforeTransition` {#htmx:beforeTransition}
This event is triggered before a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
wrapped swap occurs. If you call `preventDefault()` on the event to cancel it, the View Transition will not occur and the normal swapping logic will
happen instead.
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.boosted` - true if the request is via an element using boosting
* `detail.requestConfig` - the configuration of the AJAX request
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
* `detail.target` - the target of the swap
### Event - `htmx:configRequest` {#htmx:configRequest}
This event is triggered after htmx has collected parameters for inclusion in the request. It can be
used to include or update the parameters that htmx will send:
\`\`\`javascript
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the mix
});
\`\`\`
Note that if an input value appears more than once the value in the `parameters` object will be an array, rather
than a single value.
##### Details
* `detail.parameters` - the parameters that will be submitted in the request
* `detail.unfilteredParameters` - the parameters that were found before filtering by [`hx-params`](@/attributes/hx-params.md)
* `detail.headers` - the request headers
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.verb` - the HTTP verb in use
### Event - `htmx:confirm` {#htmx:confirm}
This event is fired on every trigger for a request (not just on elements that have a hx-confirm attribute).
It allows you to cancel (or delay) issuing the AJAX request.
If you call `preventDefault()` on the event, it will not issue the given request.
The `detail` object contains a function, `evt.detail.issueRequest(skipConfirmation=false)`, that can be used to issue the actual AJAX request at a later point.
Combining these two features allows you to create an asynchronous confirmation dialog.
Here is a basic example that shows the basic usage of the `htmx:confirm` event without altering the default behavior:
\`\`\`javascript
document.body.addEventListener('htmx:confirm', function(evt) {
// 0. To modify the behavior only for elements with the hx-confirm attribute,
// check if evt.detail.target.hasAttribute('hx-confirm')
// 1. Prevent the default behavior (this will prevent the request from being issued)
evt.preventDefault();
// 2. Do your own logic here
console.log(evt.detail)
// 3. Manually issue the request when you are ready
evt.detail.issueRequest(); // or evt.detail.issueRequest(true) to skip the built-in window.confirm()
});
\`\`\`
And here is an example using [sweet alert](https://sweetalert.js.org/guides/) on any element with a `confirm-with-sweet-alert="{question}"` attribute on it:
\`\`\`javascript
document.body.addEventListener('htmx:confirm', function(evt) {
// 1. The requirement to show the sweet alert is that the element has a confirm-with-sweet-alert
// attribute on it, if it doesn't we can return early and let the default behavior happen
if (!evt.detail.target.hasAttribute('confirm-with-sweet-alert')) return
// 2. Get the question from the attribute
const question = evt.detail.target.getAttribute('confirm-with-sweet-alert');
// 3. Prevent the default behavior (this will prevent the request from being issued)
evt.preventDefault();
// 4. Show the sweet alert
swal({
title: "Are you sure?",
text: question || "Are you sure you want to continue?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
// 5. If the user confirms, we can manually issue the request
evt.detail.issueRequest(true); // true to skip the built-in window.confirm()
}
});
});
\`\`\`
##### Details
* `detail.elt` - the element in question
* `detail.etc` - additional request information (mostly unused)
* `detail.issueRequest(skipConfirmation=false)` - a function that can be invoked to issue the request (should be paired with `evt.preventDefault()`!), if skipConfirmation is `true` the original `window.confirm()` is not executed
* `detail.path` - the path of the request
* `detail.target` - the element that triggered the request
* `detail.triggeringEvent` - the original event that triggered this request
* `detail.verb` - the verb of the request (e.g. `GET`)
* `detail.question` - the question passed to `hx-confirm` attribute (only available if `hx-confirm` attribute is present)
### Event - `htmx:historyCacheError` {#htmx:historyCacheError}
This event is triggered when an attempt to save the cache to `localStorage` fails
##### Details
* `detail.cause` - the `Exception` that was thrown when attempting to save history to `localStorage`
### Event - `htmx:historyCacheMiss` {#htmx:historyCacheMiss}
This event is triggered when a cache miss occurs when restoring history
##### Details
* `detail.xhr` - the `XMLHttpRequest` that will retrieve the remote content for restoration
* `detail.path` - the path and query of the page being restored
### Event - `htmx:historyCacheMissError` {#htmx:historyCacheMissError}
This event is triggered when a cache miss occurs and a response has been retrieved from the server
for the content to restore, but the response is an error (e.g. `404`)
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.path` - the path and query of the page being restored
### Event - `htmx:historyCacheMissLoad` {#htmx:historyCacheMissLoad}
This event is triggered when a cache miss occurs and a response has been retrieved successfully from the server
for the content to restore
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.path` - the path and query of the page being restored
### Event - `htmx:historyRestore` {#htmx:historyRestore}
This event is triggered when htmx handles a history restoration action
##### Details
* `detail.path` - the path and query of the page being restored
### Event - `htmx:beforeHistorySave` {#htmx:beforeHistorySave}
This event is triggered before the content is saved in the history api.
##### Details
* `detail.path` - the path and query of the page being restored
* `detail.historyElt` - the history element being restored into
### Event - `htmx:load` {#htmx:load}
This event is triggered when a new node is loaded into the DOM by htmx.
##### Details
* `detail.elt` - the newly added element
### Event - `htmx:noSSESourceError` {#htmx:noSSESourceError}
This event is triggered when an element refers to an SSE event in its trigger, but no parent SSE source has been defined
##### Details
* `detail.elt` - the element with the bad SSE trigger
### Event - `htmx:oobAfterSwap` {#htmx:oobAfterSwap}
This event is triggered as part of an [out of band swap](@/docs.md#oob_swaps) and behaves identically to an [after swap event](#htmx:afterSwap)
##### Details
* `detail.elt` - the swapped in element
* `detail.shouldSwap` - if the content will be swapped (defaults to `true`)
* `detail.target` - the target of the swap
* `detail.fragment` - the response fragment
### Event - `htmx:oobBeforeSwap` {#htmx:oobBeforeSwap}
This event is triggered as part of an [out of band swap](@/docs.md#oob_swaps) and behaves identically to a [before swap event](#htmx:beforeSwap)
##### Details
* `detail.elt` - the target of the swap
* `detail.shouldSwap` - if the content will be swapped (defaults to `true`)
* `detail.target` - the target of the swap
* `detail.fragment` - the response fragment
### Event - `htmx:oobErrorNoTarget` {#htmx:oobErrorNoTarget}
This event is triggered when an [out of band swap](@/docs.md#oob_swaps) does not have a corresponding element
in the DOM to switch with.
##### Details
* `detail.content` - the element with the bad oob `id`
### Event - `htmx:onLoadError` {#htmx:onLoadError}
This event is triggered when an error occurs during the `load` handling of an AJAX call
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.exception` - the exception that occurred
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:prompt` {#htmx:prompt}
This event is triggered after a prompt has been shown to the user with the [`hx-prompt`](@/attributes/hx-prompt.md)
attribute. If this event is cancelled, the AJAX request will not occur.
##### Details
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.prompt` - the user response to the prompt
### Event - `htmx:beforeHistoryUpdate` {#htmx:beforeHistoryUpdate}
This event is triggered before a history update is performed. It can be
used to modify the `path` or `type` used to update the history.
##### Details
* `detail.history` - the `path` and `type` (push, replace) for the history update
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:pushedIntoHistory` {#htmx:pushedIntoHistory}
This event is triggered after a URL has been pushed into history.
##### Details
* `detail.path` - the path and query of the URL that has been pushed into history
### Event - `htmx:replacedInHistory` {#htmx:replacedInHistory}
This event is triggered after a URL has been replaced in history.
##### Details
* `detail.path` - the path and query of the URL that has been replaced in history
### Event - `htmx:responseError` {#htmx:responseError}
This event is triggered when an HTTP error response occurs
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:sendAbort` {#htmx:sendAbort}
This event is triggered when a request is aborted
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:sendError` {#htmx:sendError}
This event is triggered when a network error prevents an HTTP request from occurring
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:sseError` {#htmx:sseError}
This event is triggered when an error occurs with an SSE source
##### Details
* `detail.elt` - the element with the bad SSE source
* `detail.error` - the error
* `detail.source` - the SSE source
### Event - `htmx:swapError` {#htmx:swapError}
This event is triggered when an error occurs during the swap phase
##### Details
* `detail.xhr` - the `XMLHttpRequest`
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:targetError` {#htmx:targetError}
This event is triggered when a bad selector is used for a [`hx-target`](@/attributes/hx-target.md) attribute (e.g. an
element ID without a preceding `#`)
##### Details
* `detail.elt` - the element that triggered the request
* `detail.target` - the bad CSS selector
### Event - `htmx:timeout` {#htmx:timeout}
This event is triggered when a request timeout occurs. This wraps the typical `timeout` event of XMLHttpRequest.
Timeout time can be set using `htmx.config.timeout` or per element using [`hx-request`](@/attributes/hx-request.md)
##### Details
* `detail.elt` - the element that dispatched the request
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
* `detail.requestConfig` - the configuration of the AJAX request
### Event - `htmx:trigger` {#htmx:trigger}
This event is triggered whenever an AJAX request would be, even if no AJAX request is specified. It
is primarily intended to allow `hx-trigger` to execute client-side scripts; AJAX requests have more
granular events available, like [`htmx:beforeRequest`](#htmx:beforeRequest) or [`htmx:afterRequest`](#htmx:afterRequest).
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:validateUrl` {#htmx:validateUrl}
This event is triggered before a request is made, allowing you to validate the URL that htmx is going to request. If
`preventDefault()` is invoked on the event, the request will not be made.
\`\`\`javascript
document.body.addEventListener('htmx:validateUrl', function (evt) {
// only allow requests to the current server as well as myserver.com
if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
evt.preventDefault();
}
});
\`\`\`
##### Details
* `detail.elt` - the element that triggered the request
* `detail.url` - the URL Object representing the URL that a request will be sent to.
* `detail.sameHost` - will be `true` if the request is to the same host as the document
### Event - `htmx:validation:validate` {#htmx:validation:validate}
This event is triggered before an element is validated. It can be used with the `elt.setCustomValidity()` method
to implement custom validation rules.
\`\`\`html
<form hx-post="/test">
<input _="on htmx:validation:validate
if my.value != 'foo'
call me.setCustomValidity('Please enter the value foo')
else
call me.setCustomValidity('')"
name="example">
</form>
\`\`\`
##### Details
* `detail.elt` - the element to be validated
### Event - `htmx:validation:failed` {#htmx:validation:failed}
This event is triggered when an element fails validation.
##### Details
* `detail.elt` - the element that failed validation
* `detail.message` - the validation error message
* `detail.validity` - the validity object, which contains properties specifying how validation failed
### Event - `htmx:validation:halted` {#htmx:validation:halted}
This event is triggered when a request is halted due to validation errors.
##### Details
* `detail.elt` - the element that triggered the request
* `detail.errors` - an array of error objects with the invalid elements and errors associated with them
### Event - `htmx:xhr:abort` {#htmx:xhr:abort}
This event is triggered when an ajax request aborts
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:xhr:loadstart` {#htmx:xhr:loadstart}
This event is triggered when an ajax request starts
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:xhr:loadend` {#htmx:xhr:loadend}
This event is triggered when an ajax request finishes
##### Details
* `detail.elt` - the element that triggered the request
### Event - `htmx:xhr:progress` {#htmx:xhr:progress}
This event is triggered periodically when an ajax request that supports progress is in flight
##### Details
* `detail.elt` - the element that triggered the request
```
# extensions/building.md
```md
+++
title = "Building htmx Extensions"
+++
To define an extension you call the `htmx.defineExtension()` function:
\`\`\`html
<script>
htmx.defineExtension('my-ext', {
onEvent : function(name, evt) {
console.log("Fired event: " + name, evt);
}
})
</script>
\`\`\`
Typically, this is done in a stand-alone javascript file, rather than in an inline `script` tag.
Extensions should have names that are dash separated and that are reasonably short and descriptive.
Extensions can override the following default extension points to add or change functionality:
\`\`\`javascript
{
init: function(api) {return null;},
getSelectors: function() {return null;},
onEvent : function(name, evt) {return true;},
transformResponse : function(text, xhr, elt) {return text;},
isInlineSwap : function(swapStyle) {return false;},
handleSwap : function(swapStyle, target, fragment, settleInfo) {return false;},
encodeParameters : function(xhr, parameters, elt) {return null;}
}
\`\`\`
```
# extensions/head-support.md
```md
+++
title = "htmx Head Tag Support Extension"
+++
The `head-support` extension adds support for [head tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
in responses to htmx requests.
htmx began as a library focused on _partial replacement_ of HTML within the `body` tag. As such, merging additional
information such as the head tag was not a focus of the library. (This is in contrast with, for example, TurboLinks,
which was focused on merging entire web pages retrieved via AJAX into the browser.)
The [`hx-boost`](@/attributes/hx-boost.md) attribute moved htmx closer to this world of full HTML-document support &
support for extracting the `title` tag out of head elements was eventually added, but full head tag support has never been
a feature of the library. This extension addresses that shortcoming.
## Installing
The fastest way to install `head-support` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-head-support@2.0.2" integrity="sha384-cvMqHzjCJsOHgGuyB3sWXaUSv/Krm0BdzjuI1rtkjCbL1l1oHJx+cHyVRJhyuEz0" crossorigin="anonymous"></script>
</head>
<body hx-ext="head-support">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-head-support/dist/head-support.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `head-support` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-head-support`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `head-support` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-head-support
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-head-support/dist/head-support.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-head-support` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-head-support`;
\`\`\`
## Usage
\`\`\`html
<body hx-ext="head-support">
...
</body>
\`\`\`
With this installed, all responses that htmx receives that contain a `head` tag in them (even if they are not complete
HTML documents with a root `<html>` element) will be processed.
How the head tag is handled depends on the type of htmx request.
If the htmx request is from a boosted element, then the following merge algorithm is used:
* Elements that exist in the current head as exact textual matches will be left in place
* Elements that do not exist in the current head will be added at the end of the head tag
* Elements that exist in the current head, but not in the new head will be removed from the head
If the htmx request is from a non-boosted element, then all content will be _appended_ to the existing head element.
If you wish to override this behavior in either case, you can place the `hx-head` attribute on the new `<head>` tag,
with either of the following two values:
* `merge` - follow the merging algorithm outlined above
* `append` - append the elements to the existing head
### Controlling Merge Behavior
Beyond this, you may also control merging behavior of individual elements with the following attributes:
* If you place `hx-head="re-eval"` on a head element, it will be re-added (removed and appended) to the head tag on every
request, even if it already exists. This can be useful to execute a script on every htmx request, for example.
* If you place `hx-preserve="true"` on an element, it will never be removed from the head
### Example
As an example, consider the following head tag in an existing document:
\`\`\`html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<link rel="stylesheet" href="/css/site1.css">
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
</head>
\`\`\`
If htmx receives a request containing this new head tag:
\`\`\`html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<link rel="stylesheet" href="/css/site2.css">
<script src="/js/script2.js"></script>
<script src="/js/script3.js"></script>
</head>
\`\`\`
Then the following operations will occur:
* `<link rel="stylesheet" href="https://the.missing.style">` will be left alone
* `<link rel="stylesheet" href="/css/site1.css">` will be removed from the head
* `<link rel="stylesheet" href="/css/site2.css">` will be added to the head
* `<script src="/js/script1.js"></script>` will be removed from the head
* `<script src="/js/script2.js"></script>` will be left alone
* `<script src="/js/script3.js"></script>` will be added to the head
The final head element will look like this:
\`\`\`html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<script src="/js/script2.js"></script>
<link rel="stylesheet" href="/css/site2.css">
<script src="/js/script3.js"></script>
</head>
\`\`\`
## Events
This extension triggers the following events:
* `htmx:removingHeadElement` - triggered when a head element is about to be removed, with the element being removed
available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be removed.
* `htmx:addingHeadElement` - triggered when a head element is about to be added, with the element being added
available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be added.
* `htmx:afterHeadMerge` - triggered after a head tag merge has occurred, with the following values available in the event `detail`:
* `added` - added head elements
* `kept` - kept head elements
* `removed` - removed head elements
* `htmx:beforeHeadMerge` - triggered before a head merge occurs
```
# extensions/htmx-1-compat.md
```md
+++
title = "htmx 1.x Compatibility Extension"
+++
The `htmx-1-compat` extension allows you to almost seamlessly upgrade from htmx 1.x to htmx 2.
## Installing
The fastest way to install `htmx-1-compat` is to load it via a CDN. Remember to always include the core htmx library before the extension and enable the extension.
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-htmx-1-compat@2.0.0" integrity="sha384-lcvVWaNjF5zPPUeeWmC0OkJ2MLqoWLlkAabuGm+EuMSTfGo5WRyHrNaAp0cJr9Pg" crossorigin="anonymous"></script>
</head>
<body hx-ext="htmx-1-compat">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-htmx-1-compat/dist/htmx-1-compat.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `htmx-1-compat` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-htmx-1-compat`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `htmx-1-compat` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-htmx-1-compat
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-htmx-1-compat/dist/htmx-1-compat.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-htmx-1-compat` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-htmx-1-compat`;
\`\`\`
## What it covers
Htmx 2 introduced a few [breaking changes](https://v2-0v2-0.htmx.org/migration-guide-htmx-1/).
To make upgrading from htmx 1.x to htmx 2 easier, we're providing this extension that reverts most of those, so you're
able to benefit from the other changes without breaking your application.
### Obsolete attributes
- htmx 2 removed the deprecated [hx-ws](https://htmx.org/attributes/hx-ws/)
and [hx-sse](https://htmx.org/attributes/hx-sse/) attributes, that this extension restores.
- htmx 2 removed the deprecated `hx-on` attribute in favor of the
wildcard [`hx-on*` attribute](https://htmx.org/attributes/hx-on/), that this extension restores.
### Default Changes
- reverts [htmx.config.scrollBehavior](https://htmx.org/reference/#config) to 'smooth'.
- makes `DELETE` requests use a form-encoded body rather than URL parameters (htmx 2 uses URL parameters for `DELETE` as
default as per [the spec](https://www.rfc-editor.org/rfc/rfc9110.html#name-delete)).
- allows cross-domain requests by default (htmx 2 now forbids it by default).
## What it does not cover
- IE11 support was dropped in htmx 2, and this extension cannot revert that. If you need IE11 support, please stay with
htmx 1 that will continue being supported.
- htmx 2 introduced the breaking change that is the [swap method](https://v2-0v2-0.htmx.org/api/#swap) to the extensions
API. If you were only using core extensions, then you shouldn't need any additional work. If you were using custom or
community extensions, make sure that they were updated to work with htmx 2's API.
```
# extensions/idiomorph.md
```md
+++
title = "htmx Idiomorph Extension"
+++
[Idiomorph](https://github.com/bigskysoftware/idiomorph) is a DOM morphing algorithm created by the htmx creator. DOM
morphing is a process where an existing DOM tree is "morphed" into the shape of another in a way that resuses as much of
the existing DOM's nodes as possible. By preserving nodes when changing from one tree to another you can present a
much smoother transition between the two states.
You can use the idiomorph morphing algorithm as a [swapping](@attributes/hx-swap) strategy by including the idiomorph
extension.
## Installing
The fastest way to install `idiomorph` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/idiomorph@0.7.3" integrity="sha384-JcorokHTL/m+D6ZHe2+yFVQopVwZ+91GxAPDyEZ6/A/OEPGEx1+MeNSe2OGvoRS9" crossorigin="anonymous"></script>
<script src="https://unpkg.com/idiomorph@0.7.3/dist/idiomorph-ext.min.js" integrity="sha384-szktAZju9fwY15dZ6D2FKFN4eZoltuXiHStNDJWK9+FARrxJtquql828JzikODob" crossorigin="anonymous"></script>
</head>
<body hx-ext="morph">
\`\`\`
Unminified versions are also available at:
https://unpkg.com/idiomorph/dist/idiomorph.js
https://unpkg.com/idiomorph/dist/idiomorph-ext.js
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `idiomorph` is to simply copy it into your project. Download idiomorph and its htmx extension from `https://unpkg.com/idiomorph` and `https://unpkg.com/idiomorph/dist/idiomorph-ext.min.js`, add them to the appropriate directory in your project and include them where necessary with `<script>` tags.
For npm-style build systems, you can install `idiomorph` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install idiomorph
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/idiomorph/dist/idiomorph.js` (or `.min.js`) and `node_modules/idiomorph/dist/idiomorph-ext.js`. For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `idiomorph` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `idiomorph`;
\`\`\`
## Usage
Once you have referenced the idiomorph extension, you can register it with the name `morph` on the body and then being
using `morph`, `morph:outerHTML` or `morph:innerHTML` as swap strategies.
* `morph` & `morph:outerHTML` will morph the target element as well as it's children
* `morph:innerHTML` will morph only the inner children of an element, leaving the target untouched
\`\`\`html
<body hx-ext="morph">
<button hx-get="/example" hx-swap="morph">
Morph My Outer HTML
</button>
<button hx-get="/example" hx-swap="morph:outerHTML">
Morph My Outer HTML
</button>
<button hx-get="/example" hx-swap="morph:innerHTML">
Morph My Inner HTML
</button>
</body>
\`\`\`
```
# extensions/preload.md
```md
+++
title = "htmx Preload Extension"
+++
The `preload` extension allows you to load HTML fragments into your browser's cache before they are requested by the
user, so that additional pages appear to users to load nearly instantaneously. As a developer, you can customize its
behavior to fit your applications needs and use cases.
**IMPORTANT:** Preloading content judiciously can improve your web application's perceived performance, but preloading
too many resources can negatively impact your visitors' bandwidth and your server performance by initiating too many
unused requests. Use this extension carefully!
## Installing
The fastest way to install `preload` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-preload@2.1.0" integrity="sha384-fkzubQiTB69M7XTToqW6tplvxAOJkqPl5JmLAbumV2EacmuJb8xEP9KnJafk/rg8" crossorigin="anonymous"></script>
</head>
<body hx-ext="preload">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-preload/dist/preload.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `preload` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-preload`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `preload` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-preload
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-preload/dist/preload.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-preload` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-preload`;
\`\`\`
## Usage
Register the extension with htmx using the `hx-ext` attribute. Then, add a `preload` attribute to any hyperlinks
and `hx-get` elements you want to preload. By default, resources will be loaded as soon as the `mousedown` event begins,
giving your application a roughly 100-200ms head start on serving responses. See configuration below for other options.
\`\`\`html
<body hx-ext="preload">
<h1>What Works</h2>
<a href="/server/1" preload>WILL BE requested using a standard XMLHttpRequest() and default options (below)</a>
<button hx-get="/server/2" preload>WILL BE requested with additional htmx headers.</button>
<h1>What WILL NOT WORK</h1>
<a href="/server/3">WILL NOT be preloaded because it does not have an explicit "preload" attribute</a>
<a hx-post="/server/4" preload>WILL NOT be preloaded because it is an HX-POST transaction.</a>
</body>
\`\`\`
All preload requests include an additional `"HX-Preloaded": "true"` header.
### Inheriting Preload Settings
You can add the `preload` attribute to the top-level element that contains several `<a href="">` or `hx-get=""`
elements, and all of them will be preloaded. Be careful with this setting, because you can end up wasting bandwidth if
you preload many more resources than you need.
\`\`\`html
<body hx-ext="preload">
<ul preload>
<li><a href="/server/1">This will be preloaded because of the attribute in the node above.</a>
<li><a href="/server/2">This will also be preloaded for the same reason.</a>
<li><a href="/server/3">This will be preloaded, too. Lorem ipsum.</a>
</ul>
</body>
\`\`\`
### Preloading Forms
The extension can preload some form elements if the form includes `hx-get` attribute or uses `method="get"`. The `preload` attribute can be added to the form or to some of its selected elements. Currently these form elements can be preloaded:
- `<input type="radio>">` will be preloaded as if the radio button was clicked and form submitted
- `<input type="checkbox">` will be preloaded as if the checkbox was checked and form submitted
- `<input type="checkbox" checked>` will be preloaded as if the checkbox was unchecked and form submitted
- `<select>` will send multiple preload requests as if each unselected option was selected and form submitted
- `<input type="submit">` will be preloaded as if form was submitted
### Preloading of Linked Images
After an HTML page (or page fragment) is preloaded, this extension can also preload linked image resources. It will not
load or run linked Javascript or Cascading Stylesheet content, whether linked or embedded in the preloaded HTML. To
preload images as well, use the following syntax.
\`\`\`html
<div hx-ext="preload">
<a href="/my-next-page" preload="mouseover" preload-images="true">Next Page</a>
</div>
\`\`\`
### Configuration
Defaults for this extension are chosen to balance users' perceived performance with potential load on your servers from
unused requests. As a developer, you can modify two settings to customize this behavior to your specific use cases.
#### preload="mousedown" (DEFAULT)
The default behavior for this extension is to begin loading a resource when the user presses the mouse down. This is a
conservative setting that guarantees the user actually intends to use the linked resource. Because user click events
typically take 100-200ms to complete, this setting gives your server a significant headstart compared with a regular
click.
\`\`\`html
<a href="/server/1" preload="mousedown">This will be preloaded when the user begins to click.</a>
\`\`\`
#### preload="mouseover"
To preload links more aggressively, you can trigger the preload to happen when the user's mouse hovers over the link
instead. To prevent many resources from being loaded when the user scrolls or moves the mouse across a large list of
objects, a 100ms delay is built in to this action. If the user's mouse leaves the element *before* this timeout expires,
then the resource is not preloaded.
Typical users hover over links for several hundred milliseconds before they click, which gives your server even more
time to respond to the request than the `mousedown` option
above. [Test your own hover timing here.](http://instantclick.io/click-test). However, be careful when using this
option because it can increase server load by requesting resources unnecessarily.
\`\`\`html
<a href="/server/1" preload="mouseover">This will be preloaded when the user's mouse remains over it for more than
100ms.</a>
\`\`\`
#### preload="custom-event-name"
Preload can also listen to any custom event within the system, triggering resources to be preloaded (if they have not
already been cached by the browser). The extension itself generates an event called `preload:init` that can be used to
trigger preloads as soon as an object has been processed by htmx.
\`\`\`html
<body hx-ext="preload">
<button hx-get="/server" preload="preload:init" hx-target="idLoadMore">Load More</a>
<div id="idLoadMore">
Content for this DIV will be preloaded as soon as the page is ready.
Clicking the button above will swap it into the DOM.
</div>
</body>
\`\`\`
#### preload="always"
By default, the extension will preload each element once.
If you would like to keep always preloading the element, you can add `preload="always"` attribute.
This can be useful if `hx-target` is not the element itself.
This attribute can be combined with other configuration attributes e.g. `preload="always mouseover"`.
### About Touch Events
To accommodate touchscreen devices, an additional `ontouchstart` event handler is added whenever you specify
a `mouseover` or `mousedown` trigger. This extra trigger fires immediately (no waiting period) whenever the user touches
the screen, saving you 300ms of waiting time on Android, and 450ms on iOS.
### Limitations
* Links must be marked with a `preload` attribute, or have an ancestor node that has the `preload` attribute.
* Only `GET` transactions (including `<a href="">` and `hx-get=""`) can be preloaded. Following REST principles, `GET`
transactions are assumed to not make any significant changes to a resource. Transactions that can potentially make a
change (such as `POST`, `PUT`, and `DELETE`) will not be preloaded under any circumstances.
* When listening to `mouseover` events, preload waits for 100ms before downloading the linked resource. If the mouse
leaves the resource before this timeout expires, the resource is not preloaded.
* Preloaded responses will only be cached in the browser if the response headers allow it. For example, the response
header `Cache-Control: private, max-age=60` allows the browser to cache the response,
whereas `Cache-Control: no-cache` prevents it.
## Credits
The behavior for this plugin was inspired by the work done by [Alexandre Dieulot](https://github.com/dieulot)
on [InstantClick](http://instantclick.io/), which is released under the MIT license.
```
# extensions/response-targets.md
```md
+++
title = "htmx Response Targets Extension"
+++
This extension allows you to specify different target elements to be swapped when
different HTTP response codes are received.
It uses attribute names in a form of ``hx-target-[CODE]`` where `[CODE]` is a numeric
HTTP response code with the optional wildcard character at its end. You can also use
`hx-target-error`, which handles both 4xx and 5xx response codes.
The value of each attribute can be:
* A CSS query selector of the element to target.
* `this` which indicates that the element that the `hx-target` attribute is on is the target.
* `closest <CSS selector>` which will find the closest parent ancestor that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g `previous .error` will target the closest previous sibling with `error` class)
## Installing
The fastest way to install `response-targets` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-response-targets@2.0.2" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous"></script>
</head>
<body hx-ext="response-targets">
...
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-response-targets/dist/response-targets.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `response-targets` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-response-targets`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `response-targets` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-response-targets
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-response-targets/dist/response-targets.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-response-targets` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-response-targets`;
\`\`\`
## Configure (optional)
* When `HX-Retarget` response header is received it disables any lookup that would be
performed by this extension but any responses with error status codes will be
swapped (normally they would not be, even with target set via header) and internal
error flag (`isError`) will be modified. You may change this and choose to ignore
`HX-Retarget` header when `hx-target-…` is in place by setting a configuration flag
`htmx.config.responseTargetPrefersRetargetHeader` to `false` (default is
`true`). Note that this extension only performs a simple check whether the header
is set and target exists. It is not extracting target's value from the header but
trusts it was set by HTMX core logic.
* Normally, any target which is already established by HTMX built-in functions or
extensions called before will be overwritten if a matching `hx-target-…` tag is
found. You may change it by using a configuration flag
`htmx.config.responseTargetPrefersExisting` to `true` (default is `false`). This is
kinky and risky option. It has a real-life applications similar to a skilled,
full-stack tardigrade eating parentheses when no one is watching.
* `isError` flag on the `detail` member of an event associated with swapping the
content with `hx-target-[CODE]` will be set to `false` when error response code is
received. This is different from the default behavior. You may change this by
setting a configuration flag `htmx.config.responseTargetUnsetsError` to `false`
(default is `true`).
* `isError` flag on the `detail` member of an event associated with swapping the
content with `hx-target-[CODE]` will be set to `false` when non-erroneous response
code is received. This is no different from the default behavior. You may change
this by setting a configuration flag `htmx.config.responseTargetSetsError` to
`true` (default is `false`). This setting will not affect the response code 200
since it is not handled by this extension.
## Usage
Here is an example that targets a `div` for normal (200) response but another `div`
for 404 (not found) response, and yet another for all 5xx response codes:
\`\`\`html
<div hx-ext="response-targets">
<div id="response-div"></div>
<button hx-post="/register"
hx-target="#response-div"
hx-target-5*="#serious-errors"
hx-target-404="#not-found">
Register!
</button>
<div id="serious-errors"></div>
<div id="not-found"></div>
</div>
\`\`\`
* The response from the `/register` URL will replace contents of the `div` with the
`id` `response-div` when response code is 200 (OK).
* The response from the `/register` URL will replace contents of the `div` with the `id`
`serious-errors` when response code begins with a digit 5 (server errors).
* The response from the `/register` URL will replace contents of the `div` with
the `id` `not-found` when response code is 404 (Not Found).
Sometimes you may not want to handle 5xx and 4xx errors separately, in which case you
can use `hx-target-error`:
\`\`\`html
<div hx-ext="response-targets">
<div id="response-div"></div>
<button hx-post="/register"
hx-target="#response-div"
hx-target-error="#any-errors">
Register!
</button>
<div id="any-errors"></div>
</div>
\`\`\`
2xx codes will be handled as in the previous example. However, when the response code is 5xx
or 4xx, the response from `/register` will replace the contents of the `div` with the `id`
`any-errors`.
## Wildcard resolution
When status response code does not match existing `hx-target-[CODE]` attribute name
then its numeric part expressed as a string is trimmed with last character being
replaced with the asterisk (`*`). This lookup process continues until the attribute
is found or there are no more digits.
For example, if a browser receives 404 error code, the following attribute names will
be looked up (in the given order):
* `hx-target-404`
* `hx-target-40*`
* `hx-target-4*`
* `hx-target-*`.
_If you are using tools that do not support asterisks in HTML attributes, you
may instead use the `x` character, e.g., `hx-target-4xx`._
## Notes
* `hx-target-…` is inherited and can be placed on a parent element.
* `hx-target-…` cannot be used to handle HTTP response code 200.
* `hx-target-…` will honor `HX-Retarget` by default and will prefer it over any
calculated target but it can be changed by disabling the
`htmx.config.responseTargetPrefersRetargetHeader` configuration option.
* To avoid surprises the `hx-ext` attribute used to enable this extension should be
placed on a parent element containing elements with `hx-target-…` and `hx-target`
attributes.
## See also
* [`hx-target`](https://htmx.org/attributes/hx-target), specifies the target element to be swapped
```
# extensions/sse.md
```md
+++
title = "htmx Server Sent Event (SSE) Extension"
+++
The `Server Sent Events` extension connects to
an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) directly
from HTML. It manages the connections to your web server, listens for server events, and then swaps their contents into
your htmx webpage in real-time.
SSE is a lightweight alternative to WebSockets that works over existing HTTP connections, so it is easy to use through
proxy servers and firewalls. Remember, SSE is a uni-directional service, so you cannot send any messages to an SSE
server once the connection has been established. If you need bi-directional communication, then you should consider
using [WebSockets](@/extensions/ws.md) instead.
This extension replaces the experimental `hx-sse` attribute built into previous versions of htmx. For help migrating
from older versions, see the migration guide at the bottom of this page.
Use the following attributes to configure how SSE connections behave:
* `sse-connect="<url>"` - The URL of the SSE server.
* `sse-swap="<message-name>"` - The name of the message to swap into the DOM.
* `hx-trigger="sse:<message-name>"` - SSE messages can also trigger HTTP callbacks using
the [`hx-trigger`](https://htmx.org/attributes/hx-trigger) attribute.
* `sse-close=<message-name>` - To close the EventStream gracefully when that message is received. This might be helpful
if you want to send information to a client that will eventually stop.
## Installing
The fastest way to install `sse` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-sse@2.2.2" integrity="sha384-Y4gc0CK6Kg+hmulDc6rZPJu0tqvk7EWlih0Oh+2OkAi1ZDlCbBDCQEE2uVk472Ky" crossorigin="anonymous"></script>
</head>
<body hx-ext="sse">
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-sse/dist/sse.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `sse` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-sse`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `sse` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-sse
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-sse/dist/sse.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-sse` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-sse`;
\`\`\`
## Usage
\`\`\`html
<div hx-ext="sse" sse-connect="/chatroom" sse-swap="message">
Contents of this box will be updated in real time
with every SSE message received from the chatroom.
</div>
\`\`\`
### Connecting to an SSE Server
To connect to an SSE server, use the `hx-ext="sse"` attribute to install the extension on that HTML element, then
add `sse-connect="<url>"` to the element to make the connection.
When designing your server application, remember that SSE works just like any HTTP request. Although you cannot send any
messages to the server after you have established a connection, you can send parameters to the server along with your
request. So, instead of making an SSE connection to your server at `https://my-server/chat-updates` you can also connect
to `https://my-server/chat-updates?friends=true&format=detailed`. This allows your server to customize its responses to
what your client needs.
### Receiving Named Events
SSE messages consist of an event name and a data packet. No other metadata is allowed in the message. Here is an
example:
\`\`\`txt
event: EventName
data: <div>Content to swap into your HTML page.</div>
\`\`\`
We'll use the `sse-swap` attribute to listen for this event and swap its contents into our webpage.
\`\`\`html
<div hx-ext="sse" sse-connect="/event-source" sse-swap="EventName"></div>
\`\`\`
Notice that the name `EventName` from the server's message must match the value in the `sse-swap` attribute. Your server
can use as many different event names as necessary, but be careful: browsers can only listen for events that have been
explicitly named. So, if your server sends an event named `ChatroomUpdate` but your browser is only listening for events
named `ChatUpdate` then the extra event will be discarded.
### Receiving Unnamed Events
SSE messages can also be sent without any event name. In this case, the browser uses the default name `message` in its
place. The same rules specified above still apply. If your server sends an unnamed message, then you must listen for it
by including `sse-swap="message"`. There is no option for using a catch-all name. Here's how this looks:
\`\`\`txt
data: <div>Content to swap into your HTML page.</div>
\`\`\`
\`\`\`html
<div hx-ext="sse" sse-connect="/event-source" sse-swap="message"></div>
\`\`\`
### Receiving Multiple Events
You can also listen to multiple events (named or unnamed) from a single EventSource. Listeners must be either 1) the
same element that contains the `hx-ext` and `sse-connect` attributes, or 2) child elements of the element containing
the `hx-ext` and `sse-connect` attributes.
\`\`\`html
Multiple events in the same element
<div hx-ext="sse" sse-connect="/server-url" sse-swap="event1,event2"></div>
Multiple events in different elements (from the same source).
<div hx-ext="sse" sse-connect="/server-url">
<div sse-swap="event1"></div>
<div sse-swap="event2"></div>
</div>
\`\`\`
### Trigger Server Callbacks
When a connection for server sent events has been established, child elements can listen for these events by using the
special [`hx-trigger`](https://htmx.org/attributes/hx-trigger) syntax `sse:<event_name>`. This, when combined with
an `hx-get` or similar will trigger the element to make a request.
Here is an example:
\`\`\`html
<div hx-ext="sse" sse-connect="/event_stream">
<div hx-get="/chatroom" hx-trigger="sse:chatter">
...
</div>
</div>
\`\`\`
This example establishes an SSE connection to the `event_stream` end point which then triggers
a `GET` to the `/chatroom` url whenever the `chatter` event is seen.
### Automatic Reconnection
If the SSE Event Stream is closed unexpectedly, browsers are supposed to attempt to reconnect automatically. However, in
rare situations this does not work and your browser can be left hanging. This extension adds its own reconnection
logic (using an [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff)) on top of the
browser's automatic reconnection, so that your SSE streams will always be as reliable as possible.
### Testing SSE Connections with the Demo Server
Htmx includes a demo SSE server written in Node.js that will help you to see SSE in action, and begin bootstrapping your
own SSE code. It is located in the /test/ws-sse folder of
the [`htmx-extensions`](https://github.com/bigskysoftware/htmx-extensions) repository. Look at /test/ws-sse/README.md
for instructions on running and using the test server.
### Migrating from Previous Versions
Previous versions of htmx used a built-in tag `hx-sse` to implement Server Sent Events. This code has been migrated into
an extension instead. Here are the steps you need to take to migrate to this version:
| Old Attribute | New Attribute | Comments |
|--------------------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `hx-sse=""` | `hx-ext="sse"` | Use the `hx-ext="sse"` attribute to install the SSE extension into any HTML element. |
| `hx-sse="connect:<url>"` | `sse-connect="<url>"` | Add a new attribute `sse-connect` to the tag that specifies the URL of the Event Stream. This attribute must be in the same tag as the `hx-ext` attribute. |
| `hx-sse="swap:<EventName>"` | `sse-swap="<EventName>"` | Add a new attribute `sse-swap` to any elements that will be swapped in via the SSE extension. This attribute must be placed **on** or **inside of** the tag containing the `hx-ext` attribute. |
| `hx-trigger="sse:<EventName>"` | NO CHANGE | any `hx-trigger` attributes do not need to change. The extension will identify these attributes and add listeners for any events prefixed with `sse:` |
### Listening to events dispatched by this extension
This extension dispatches several events. You can listen for these events like so:
\`\`\`javascript
document.body.addEventListener('htmx:sseBeforeMessage', function (e) {
// do something before the event data is swapped in
})
\`\`\`
Each event object has a `detail` field that contains details of the event.
#### `htmx:sseOpen`
This event is dispatched when an SSE connection has been successfully established.
##### Details
* `detail.elt` - The element on which the SSE connection was setup. This is the element which has the `sse-connect`
attribute.
* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) object.
#### `htmx:sseError`
This event is dispatched when an SSE connection could not be established.
##### Details
* `detail.error` - The error that occurred while creating
an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
#### `htmx:sseBeforeMessage`
This event is dispatched just before the SSE event data is swapped into the DOM. If you don't want to swap
call `preventDefault()` on the event. Additionally the `detail` field is
a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created
by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.
##### Details
* `detail.elt` - The swap target.
#### `htmx:sseMessage`
This event is dispatched after the SSE event data has been swapped into the DOM. The `detail` field is
a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created
by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.
#### `htmx:sseClose`
This event is dispatched in three different closing scenario. To control for the scenario the user can control for the
evt.detail.sseclose property.
\`\`\`javascript
document.body.addEventListener('htmx:sseClose', function (e) {
const reason = e.detail.type
switch (reason) {
case "nodeMissing":
// Parent node is missing and therefore connection was closed
...
case "nodeReplaced":
// Parent node replacement caused closing of connection
...
case "message":
// connection was closed due to reception of message sse-close
...
}
})
\`\`\`
##### Details
* `detail.elt` - The swap target.
### Additional SSE Resources
* [Wikipedia](https://en.wikipedia.org/wiki/Server-sent_events)
* [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
* [Can I Use?](https://caniuse.com/eventsource)
```
# extensions/ws.md
```md
+++
title = "htmx Web Socket extension"
+++
The Web Sockets extension enables easy, bi-directional communication
with [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
servers directly from HTML. This replaces the experimental `hx-ws` attribute built into previous versions of htmx. For
help migrating from older versions, see the [Migrating](#migrating-from-previous-versions) guide at the bottom of this
page.
Use the following attributes to configure how WebSockets behave:
* `ws-connect="<url>"` or `ws-connect="<prefix>:<url>"` - A URL to establish a `WebSocket` connection against.
* Prefixes `ws` or `wss` can optionally be specified. If not specified, HTMX defaults to adding the location's
scheme-type,
host and port to have browsers send cookies via websockets.
* `ws-send` - Sends a message to the nearest websocket based on the trigger value for the element (either the natural
event
or the event specified by [`hx-trigger`])
## Installing
The fastest way to install `ws` is to load it via a CDN. Remember to always include the core htmx library before the extension and [enable the extension](#usage).
\`\`\`HTML
<head>
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx-ext-ws@2.0.2" integrity="sha384-vuKxTKv5TX/b3lLzDKP2U363sOAoRo5wSvzzc3LJsbaQRSBSS+3rKKHcOx5J8doU" crossorigin="anonymous"></script>
</head>
<body hx-ext="ws">
\`\`\`
An unminified version is also available at https://unpkg.com/htmx-ext-ws/dist/ws.js.
While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install `ws` is to simply copy it into your project. Download the extension from `https://unpkg.com/htmx-ext-ws`, add it to the appropriate directory in your project and include it where necessary with a `<script>` tag.
For npm-style build systems, you can install `ws` via [npm](https://www.npmjs.com/):
\`\`\`shell
npm install htmx-ext-ws
\`\`\`
After installing, you'll need to use appropriate tooling to bundle `node_modules/htmx-ext-ws/dist/ws.js` (or `.min.js`). For example, you might bundle the extension with htmx core from `node_modules/htmx.org/dist/htmx.js` and project-specific code.
If you are using a bundler to manage your javascript (e.g. Webpack, Rollup):
- Install `htmx.org` and `htmx-ext-ws` via npm
- Import both packages to your `index.js`
\`\`\`JS
import `htmx.org`;
import `htmx-ext-ws`;
\`\`\`
## Usage
\`\`\`html
<div hx-ext="ws" ws-connect="/chatroom">
<div id="notifications"></div>
<div id="chat_room">
...
</div>
<form id="form" ws-send>
<input name="chat_message">
</form>
</div>
\`\`\`
### Configuration
WebSockets extension support two configuration options:
- `createWebSocket` - a factory function that can be used to create a custom WebSocket instances. Must be a function,
returning `WebSocket` object
- `wsBinaryType` - a string value, that defines
socket's [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType) property. Default value
is `blob`
### Receiving Messages from a WebSocket
The example above establishes a WebSocket to the `/chatroom` end point. Content that is sent down from the websocket
will
be parsed as HTML and swapped in by the `id` property, using the same logic
as [Out of Band Swaps](https://htmx.org/attributes/hx-swap-oob/).
As such, if you want to change the swapping method (e.g., append content at the end of an element or delegate swapping
to an extension),
you need to specify that in the message body, sent by the server.
\`\`\`html
<!-- will be interpreted as hx-swap-oob="true" by default -->
<form id="form">
...
</form>
<!-- will be appended to #notifications div -->
<div id="notifications" hx-swap-oob="beforeend">
New message received
</div>
<!-- will be swapped using an extension -->
<div id="chat_room" hx-swap-oob="morphdom">
....
</div>
\`\`\`
### Sending Messages to a WebSocket
In the example above, the form uses the `ws-send` attribute to indicate that when it is submitted, the form values
should be **serialized as JSON**
and send to the nearest enclosing `WebSocket`, in this case the `/chatroom` endpoint.
The serialized values will include a field, `HEADERS`, that includes the headers normally submitted with an htmx
request.
### Automatic Reconnection
If the WebSocket is closed unexpectedly, due to `Abnormal Closure`, `Service Restart` or `Try Again Later`, this
extension will attempt to reconnect until the connection is reestablished.
By default, the extension uses a
full-jitter [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) that chooses a randomized
retry delay that grows exponentially over time. You can use a different algorithm by writing it
into `htmx.config.wsReconnectDelay`. This function takes a single parameter, the number of retries, and returns the
time (in milliseconds) to wait before trying again.
\`\`\`javascript
// example reconnect delay that you shouldn't use because
// it's not as good as the algorithm that's already in place
htmx.config.wsReconnectDelay = function (retryCount) {
return retryCount * 1000 // return value in milliseconds
}
\`\`\`
The extension also implements a simple queuing mechanism that keeps messages in memory when the socket is not in `OPEN`
state and sends them once the connection is restored.
### Events
WebSockets extensions exposes a set of events that allow you to observe and customize its behavior.
#### Event - `htmx:wsConnecting` {#htmx:wsConnecting}
This event is triggered when a connection to a WebSocket endpoint is being attempted.
##### Details
* `detail.event.type` - the type of the event (`'connecting'`)
#### Event - `htmx:wsOpen` {#htmx:wsOpen}
This event is triggered when a connection to a WebSocket endpoint has been established.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.event` - the original event from the socket
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsClose` {#htmx:wsClose}
This event is triggered when a connection to a WebSocket endpoint has been closed normally.
You can check if the event was caused by an error by inspecting `detail.event` property.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.event` - the original event from the socket
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsError` {#htmx:wsError}
This event is triggered when `onerror` event on a socket is raised.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.error` - the error object
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsBeforeMessage` {#htmx:wsBeforeMessage}
This event is triggered when a message has just been received by a socket, similar to `htmx:beforeOnLoad`. This event
fires
before any processing occurs.
If the event is cancelled, no further processing will occur.
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.message` - raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsAfterMessage` {#htmx:wsAfterMessage}
This event is triggered when a message has been completely processed by htmx and all changes have been
settled, similar to `htmx:afterOnLoad`.
Cancelling this event has no effect.
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.message` - raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsConfigSend` {#htmx:wsConfigSend}
This event is triggered when preparing to send a message from `ws-send` element.
Similarly to [`htmx:configRequest`](https://htmx.org/events#htmx:configRequest), it allows you to modify the message
before sending.
If the event is cancelled, no further processing will occur and no messages will be sent.
##### Details
* `detail.parameters` - the parameters that will be submitted in the request
* `detail.unfilteredParameters` - the parameters that were found before filtering
by [`hx-params`](https://htmx.org/attributes/hx-params)
* `detail.headers` - the request headers. Will be attached to the body in `HEADERS` property, if not falsy
* `detail.errors` - validation errors. Will prevent sending and
trigger [`htmx:validation:halted`](https://htmx.org/events#htmx:validation:halted) event if not empty
* `detail.triggeringEvent` - the event that triggered sending
* `detail.messageBody` - raw message body that will be sent to the socket. Undefined, can be set to value of any type,
supported by WebSockets. If set, will override
default JSON serialization. Useful, if you want to use some other format, like XML or MessagePack
* `detail.elt` - the element that dispatched the sending (the one with `ws-send` attribute)
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsBeforeSend` {#htmx:wsBeforeSend}
This event is triggered just before sending a message. This includes messages from the queue.
Message can not be modified at this point.
If the event is cancelled, the message will be discarded from the queue and not sent.
##### Details
* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute)
* `detail.message` - the raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsAfterSend` {#htmx:wsAfterSend}
This event is triggered just after sending a message. This includes messages from the queue.
Cancelling the event has no effect.
##### Details
* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute)
* `detail.message` - the raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Socket wrapper
You may notice that all events expose `detail.socketWrapper` property. This wrapper holds the socket
object itself and the message queue. It also encapsulates reconnection algorithm. It exposes a few members:
- `send(message, fromElt)` - sends a message safely. If the socket is not open, the message will be persisted in the
queue
instead and sent when the socket is ready.
- `sendImmediately(message, fromElt)` - attempts to send a message regardless of socket state, bypassing the queue. May
fail
- `queue` - an array of messages, awaiting in the queue.
This wrapper can be used in your event handlers to monitor and manipulate the queue (e.g., you can reset the queue when
reconnecting), and to send additional messages (e.g., if you want to send data in batches).
The `fromElt` parameter is optional and, when specified, will trigger corresponding websocket events from
specified element, namely `htmx:wsBeforeSend` and `htmx:wsAfterSend` events when sending your messages.
### Testing with the Demo Server
Htmx includes a demo WebSockets server written in Node.js that will help you to see WebSockets in action, and begin
bootstrapping your own WebSockets code. It is located in the /test/ws-sse folder of
the [`htmx-extensions`](https://github.com/bigskysoftware/htmx-extensions) repository. Look at /test/ws-sse/README.md
for instructions on running and using the test server.
### Migrating from Previous Versions
Previous versions of htmx used a built-in tag `hx-ws` to implement WebSockets. This code has been migrated into an
extension instead. Here are the steps you need to take to migrate to this version:
| Old Attribute | New Attribute | Comments |
|-------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------|
| `hx-ws=""` | `hx-ext="ws"` | Use the `hx-ext="ws"` attribute to install the WebSockets extension into any HTML element. |
| `hx-ws="connect:<url>"` | `ws-connect="<url>"` | Add a new attribute `ws-connect` to the tag that defines the extension to specify the URL of the WebSockets server you're using. |
| `hx-ws="send"` | `ws-send=""` | Add a new attribute `ws-send` to mark any child forms that should send data to your WebSocket server |
```
# headers/hx-location.md
```md
+++
title = "HX-Location Response Header"
description = """\
Use the HX-Location response header in htmx to trigger a client-side redirection without reloading the whole page."""
+++
This response header can be used to trigger a client side redirection without reloading the whole page. Instead of changing the page's location it will act like following a [`hx-boost` link](@/attributes/hx-boost.md), creating a new history entry, issuing an ajax request to the value of the header and pushing the path into history.
A sample response would be:
\`\`\`html
HX-Location: /test
\`\`\`
Which would push the client to test as if the user had clicked on `<a href="/test" hx-boost="true">`
If you want to redirect to a specific target on the page rather than the default of document.body, you can pass more details along with the event, by using JSON for the value of the header:
\`\`\`html
HX-Location: {"path":"/test2", "target":"#testdiv"}
\`\`\`
Path is required and is url to load the response from. The rest of the data mirrors the [`ajax` api](@/api.md#ajax) context, which is:
* `source` - the source element of the request
* `event` - an event that "triggered" the request
* `handler` - a callback that will handle the response HTML
* `target` - the target to swap the response into
* `swap` - how the response will be swapped in relative to the target
* `values` - values to submit with the request
* `headers` - headers to submit with the request
* `select` - allows you to select the content you want swapped from a response
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-push-url.md
```md
+++
title = "HX-Push-Url Response Header"
description = """\
Use the HX-Push-Url response header in htmx to push a URL into the browser location history."""
+++
The `HX-Push-Url` header allows you to push a URL into the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
This creates a new history entry, allowing navigation with the browser’s back and forward buttons.
This is similar to the [`hx-push-url` attribute](@/attributes/hx-push-url.md).
If present, this header overrides any behavior defined with attributes.
The possible values for this header are:
1. A URL to be pushed into the location bar.
This may be relative or absolute, as per [`history.pushState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState).
2. `false`, which prevents the browser’s history from being updated.
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-push.md
```md
+++
title = "HX-Push Response Header (Deprecated)"
description = """\
The HX-Push response header in htmx is deprecated. Use HX-Push-Url instead."""
+++
The `HX-Push` header has been replaced by [`HX-Push-Url`](@/headers/hx-push-url.md)
```
# headers/hx-redirect.md
```md
+++
title = "HX-Redirect Response Header"
description = """\
Use the HX-Redirect response header in htmx to trigger a client-side redirection that will perform a full page \
reload."""
+++
This response header can be used to trigger a client side redirection to a new url that will do a full reload of the whole page. It uses the browser to redirect to the new location which can be useful when redirecting to non htmx endpoints that may contain different HTML `head` content or scripts. See [`HX-Location`](@/headers/hx-location.md) if you want more control over the redirect or want to use ajax requests instead of full browser reloads.
A sample response would be:
\`\`\`html
HX-Redirect: /test
\`\`\`
Which would push the client to test as if the user had entered this url manually or clicked on a non-boosted link `<a href="/test">`
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-replace-url.md
```md
+++
title = "HX-Replace-Url Response Header"
description = """\
Use the HX-Replace-Url response header in htmx to replace the current URL in the browser location history without \
creating a new history entry."""
+++
The `HX-Replace-Url` header allows you to replace the current URL in the browser [location history](https://developer.mozilla.org/en-US/docs/Web/API/History_API).
This does not create a new history entry; in effect, it removes the previous current URL from the browser&rsquo;s history.
This is similar to the [`hx-replace-url` attribute](@/attributes/hx-replace-url.md).
If present, this header overrides any behavior defined with attributes.
The possible values for this header are:
1. A URL to replace the current URL in the location bar.
This may be relative or absolute, as per [`history.replaceState()`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState), but must have the same origin as the current URL.
2. `false`, which prevents the browser’s current URL from being updated.
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# headers/hx-trigger.md
```md
+++
title = "HX-Trigger Response Headers"
description = """\
Use the HX-Trigger family of response headers in htmx to trigger client-side actions from an htmx response."""
+++
These response headers can be used to trigger client side actions on the target element within a response to htmx. You
can trigger a single event or as many uniquely named events as you would like.
The headers are:
* `HX-Trigger` - trigger events as soon as the response is received.
* `HX-Trigger-After-Settle` - trigger events after the [settling step](@/docs.md#request-operations).
* `HX-Trigger-After-Swap` - trigger events after the [swap step](@/docs.md#request-operations).
To trigger a single event with no additional details you can simply send the event name in a header like so:
`HX-Trigger: myEvent`
This will trigger `myEvent` on the triggering element and will bubble up to the body. As an example you could
listen for this event like this:
\`\`\`javascript
document.body.addEventListener("myEvent", function(evt){
alert("myEvent was triggered!");
})
\`\`\`
... or like this, if you're trying to trigger some element without using JS code:
\`\`\`html
<!-- Since it bubbles up to the <body>, we must use the `from:body` modifier below -->
<div hx-trigger="myEvent from:body" hx-get="/example"></div>
\`\`\`
If you want to pass details along with the event, you can move to JSON for the value of the trigger:
`HX-Trigger: {"showMessage":"Here Is A Message"}`
To handle this event you would write the following code:
\`\`\`javascript
document.body.addEventListener("showMessage", function(evt){
alert(evt.detail.value);
})
\`\`\`
Note that the value of the message was put into the `detail.value` slot. If you wish to pass multiple pieces of data
you can use a nested JSON object on the right hand side of the JSON object:
`HX-Trigger: {"showMessage":{"level" : "info", "message" : "Here Is A Message"}}`
And handle this event like so:
\`\`\`javascript
document.body.addEventListener("showMessage", function(evt){
if(evt.detail.level === "info"){
alert(evt.detail.message);
}
})
\`\`\`
Each property of the JSON object on the right hand side will be copied onto the details object for the event.
### Targeting Other Elements
You can trigger events on other target elements by adding a `target` argument to the JSON object.
`HX-Trigger: {"showMessage":{"target" : "#otherElement"}}`
### Multiple Triggers
If you wish to invoke multiple events, you can simply add additional properties to the top level JSON
object:
`HX-Trigger: {"event1":"A message", "event2":"Another message"}`
You may also trigger multiple events with no additional details by sending event names separated by commas, like so:
`HX-Trigger: event1, event2`
Using events gives you a lot of flexibility to add functionality to normal htmx responses.
## Notes
Response headers are not processed on 3xx response codes. see [Response Headers](@/docs.md#response-headers)
```
# QUIRKS.md
```md
+++
title = "htmx quirks"
date = 2024-12-23
updated = 2024-12-23
[taxonomies]
author = ["Carson Gross"]
tag = ["posts"]
+++
This is a "quirks" page, based on [SQLite's "Quirks, Caveats, and Gotchas In SQLite" page](https://www.sqlite.org/quirks.html).
## Attribute Inheritance
Many attributes in htmx are [inherited](@/docs.md#inheritance): child elements can receive behavior from attributes located
on parent elements.
As an example, here are two htmx-powered buttons that inherit their [target](@/attributes/hx-target.md) from a parent
div:
\`\`\`html
<div hx-target="#output">
<button hx-post="/items/100/like">Like</button>
<button hx-delete="/items/100">Delete</button>
</div>
<output id="output"></output>
\`\`\`
This helps avoid repeating attributes, thus keeping code [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
On the other hand, as the attributes get further away elements, you lose [Locality of Behavior](@/essays/locality-of-behaviour.md)
and it becomes more difficult to understand what an element is doing.
It is also possible to inadvertently change the behavior of elements by adding attributes to parents.
Some people prefer to disable inheritance in htmx entirely, using the `htmx.config.disableInheritance`
[configuration variable](@/docs.md#config).
Here is a `meta` tag configuration that does so:
\`\`\`html
<meta name="htmx-config" content='{"disableInheritance":true}'>
\`\`\`
## The Default Swap Strategy is `innerHTML`
The [`hx-swap`](@/attributes/hx-swap.md) attribute allows you to control how a swap is performed. The default strategy is
`innerHTML`, that is, to place the response HTML content within the target element.
Many people prefer to use the `outerHTML` strategy as the default instead.
You can change this behavior using the `htmx.config.defaultSwapStyle`
[configuration variable](@/docs.md#config).
Here is a `meta` tag configuration that does so:
\`\`\`html
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
\`\`\`
## Targeting the `body` Always Performs an innerHTML Swap
For historical reasons, if you target the `body` element, htmx will
[always perform an `innerHTML` swap](https://github.com/bigskysoftware/htmx/blob/fb78106dc6ef20d3dfa7e54aca20408c4e4336fc/src/htmx.js#L1696).
This means you cannot change attributes on the `body` tag via an htmx request.
## By Default `4xx` & `5xx` Responses Do Not Swap
htmx has never swapped "error" status response codes (`400`s & `500`s) by default.
This behavior annoys some people, and some server frameworks, in particular, will return a `422 - Unprocessable Entity`
response code to indicate that a form was not filled out properly.
This can be very confusing when it is first encountered.
You can configure the response behavior of htmx via the [`htmx:beforeSwap`](@/docs.md#modifying_swapping_behavior_with_events)
event or [via the `htmx.config.responseHandling` config array](https://htmx.org/docs/#response-handling).
Here is the default configuration:
\`\`\`json
{
"responseHandling": [
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": false}]
}
\`\`\`
Note that `204 No Content` also is not swapped.
If you want to swap everything regardless of response code, you can use this configuration:
\`\`\`json
{
"responseHandling": [
{"code":"...", "swap": true}]
}
\`\`\`
If you want to specifically allow `422` responses to swap, you can use this configuration:
\`\`\`json
{
"responseHandling": [
{"code":"422", "swap": true},
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": false}]
}
\`\`\`
Here is a meta tag allowing all responses to swap:
\`\`\`html
<meta name="htmx-config" content='{"responseHandling": [{"code":"...", "swap": true}]}'>
\`\`\`
## `GET` Requests on Non-Form Elements Do Not Include Form Values by Default
If a non-form element makes a non-`GET` request (e.g. a `PUT` request) via htmx, the values of the enclosing form
of that element (if any) [will be included in the request](@/docs.md#parameters).
However, if the element issues a `GET`, the values of an enclosing form will
[not be included.](https://github.com/bigskysoftware/htmx/blob/fb78106dc6ef20d3dfa7e54aca20408c4e4336fc/src/htmx.js#L3525)
If you wish to include the values of the enclosing form when issuing an `GET` you can use the
[`hx-include`](@/attributes/hx-include.md) attribute like so:
\`\`\`html
<button hx-get="/search"
hx-include="closest form">
Search
</button>
\`\`\`
## History Can Be Tricky
htmx provides support for interacting with the browser's [history](@/docs.md#history). This can be very powerful, but it
can also be tricky, particularly if you are using 3rd party JavaScript libraries that modify the DOM.
There can also be [security concerns](@/docs.md#hx-history) when using htmx's history support.
Most of these issues can be solved by disabling any local history cache and simply issuing a server request when a
user navigates backwards in history, with the tradeoff that history navigation will be slower.
Here is a meta tag that disables history caching:
\`\`\`html
<meta name="htmx-config" content='{"historyCacheSize": 0}'>
\`\`\`
## Some People Don't Like `hx-boost`
[`hx-boost`](@/attributes/hx-boost.md) is an odd feature compared with most other aspects of htmx: it "magically" turns
all anchor tags and forms into AJAX requests.
This can speed the feel of these interactions up, and also allows the forms and anchors to continue working when
[JavaScript is disabled](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), however it comes
with some tradeoffs:
* The history issues mentioned above can show up
* Only the body of the web page will be updated, so any styles and scripts in the new page `head` tag will be discarded
* The global javascript scope is not refreshed, so it is possible to have strange interactions between pages. For example
a global `let` may start failing because a symbol is already defined.
Some members on the core htmx team feel that, due to these issues, as well as the fact that browsers have improved
quite a bit in page navigation, it is best to avoid `hx-boost` and
[just use unboosted links and forms](https://unplannedobsolescence.com/blog/less-htmx-is-more/).
There is no doubt that `hx-boost` is an odd-man out when compared to other htmx attributes and suffers from the dictum
that "If something magically works, then it can also magically break."
Despite this fact, I (Carson) still feel it is useful in many situations, and it is used on the <https://htmx.org>
website.
## The JavaScript API Is Not A Focus
htmx is a hypermedia-oriented front end library. This means that htmx enhances HTML via
[attributes](@/reference.md#attributes) in the HTML , rather than providing an elaborate
JavaScript API.
There _is_ a [JavaScript API](@/reference.md#api), but it is not a focus of the library and, in most cases,
should not be used heavily by htmx end users.
If you find yourself using it heavily, especially the [`htmx.ajax()`](@/api.md#ajax) method, it may be
worth asking yourself if there is a more htmx-ish approach to achieve what you are doing.
```
# reference.md
```md
+++
title = "Reference"
+++
## Contents
* [htmx Core Attributes](#attributes)
* [htmx Additional Attributes](#attributes-additional)
* [htmx CSS Classes](#classes)
* [htmx Request Headers](#request_headers)
* [htmx Response Headers](#response_headers)
* [htmx Events](#events)
* [htmx Extensions](/extensions)
* [JavaScript API](#api)
* [Configuration Options](#config)
## Core Attribute Reference {#attributes}
The most common attributes when using htmx.
<div class="info-table">
| Attribute | Description |
|--------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| [`hx-get`](@/attributes/hx-get.md) | issues a `GET` to the specified URL |
| [`hx-post`](@/attributes/hx-post.md) | issues a `POST` to the specified URL |
| [`hx-on*`](@/attributes/hx-on.md) | handle events with inline scripts on elements |
| [`hx-push-url`](@/attributes/hx-push-url.md) | push a URL into the browser location bar to create history |
| [`hx-select`](@/attributes/hx-select.md) | select content to swap in from a response |
| [`hx-select-oob`](@/attributes/hx-select-oob.md) | select content to swap in from a response, somewhere other than the target (out of band) |
| [`hx-swap`](@/attributes/hx-swap.md) | controls how content will swap in (`outerHTML`, `beforeend`, `afterend`, ...) |
| [`hx-swap-oob`](@/attributes/hx-swap-oob.md) | mark element to swap in from a response (out of band) |
| [`hx-target`](@/attributes/hx-target.md) | specifies the target element to be swapped |
| [`hx-trigger`](@/attributes/hx-trigger.md) | specifies the event that triggers the request |
| [`hx-vals`](@/attributes/hx-vals.md) | add values to submit with the request (JSON format) |
</div>
## Additional Attribute Reference {#attributes-additional}
All other attributes available in htmx.
<div class="info-table">
| Attribute | Description |
|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| [`hx-boost`](@/attributes/hx-boost.md) | add [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement) for links and forms |
| [`hx-confirm`](@/attributes/hx-confirm.md) | shows a `confirm()` dialog before issuing a request |
| [`hx-delete`](@/attributes/hx-delete.md) | issues a `DELETE` to the specified URL |
| [`hx-disable`](@/attributes/hx-disable.md) | disables htmx processing for the given node and any children nodes |
| [`hx-disabled-elt`](@/attributes/hx-disabled-elt.md) | adds the `disabled` attribute to the specified elements while a request is in flight |
| [`hx-disinherit`](@/attributes/hx-disinherit.md) | control and disable automatic attribute inheritance for child nodes |
| [`hx-encoding`](@/attributes/hx-encoding.md) | changes the request encoding type |
| [`hx-ext`](@/attributes/hx-ext.md) | extensions to use for this element |
| [`hx-headers`](@/attributes/hx-headers.md) | adds to the headers that will be submitted with the request |
| [`hx-history`](@/attributes/hx-history.md) | prevent sensitive data being saved to the history cache |
| [`hx-history-elt`](@/attributes/hx-history-elt.md) | the element to snapshot and restore during history navigation |
| [`hx-include`](@/attributes/hx-include.md) | include additional data in requests |
| [`hx-indicator`](@/attributes/hx-indicator.md) | the element to put the `htmx-request` class on during the request |
| [`hx-inherit`](@/attributes/hx-inherit.md) | control and enable automatic attribute inheritance for child nodes if it has been disabled by default |
| [`hx-params`](@/attributes/hx-params.md) | filters the parameters that will be submitted with a request |
| [`hx-patch`](@/attributes/hx-patch.md) | issues a `PATCH` to the specified URL |
| [`hx-preserve`](@/attributes/hx-preserve.md) | specifies elements to keep unchanged between requests |
| [`hx-prompt`](@/attributes/hx-prompt.md) | shows a `prompt()` before submitting a request |
| [`hx-put`](@/attributes/hx-put.md) | issues a `PUT` to the specified URL |
| [`hx-replace-url`](@/attributes/hx-replace-url.md) | replace the URL in the browser location bar |
| [`hx-request`](@/attributes/hx-request.md) | configures various aspects of the request |
| [`hx-sync`](@/attributes/hx-sync.md) | control how requests made by different elements are synchronized |
| [`hx-validate`](@/attributes/hx-validate.md) | force elements to validate themselves before a request |
| [`hx-vars`](@/attributes/hx-vars.md) | adds values dynamically to the parameters to submit with the request (deprecated, please use [`hx-vals`](@/attributes/hx-vals.md)) |
</div>
## CSS Class Reference {#classes}
<div class="info-table">
| Class | Description |
|-----------|-------------|
| `htmx-added` | Applied to a new piece of content before it is swapped, removed after it is settled.
| `htmx-indicator` | A dynamically generated class that will toggle visible (opacity:1) when a `htmx-request` class is present
| `htmx-request` | Applied to either the element or the element specified with [`hx-indicator`](@/attributes/hx-indicator.md) while a request is ongoing
| `htmx-settling` | Applied to a target after content is swapped, removed after it is settled. The duration can be modified via [`hx-swap`](@/attributes/hx-swap.md).
| `htmx-swapping` | Applied to a target before any content is swapped, removed after it is swapped. The duration can be modified via [`hx-swap`](@/attributes/hx-swap.md).
</div>
## HTTP Header Reference {#headers}
### Request Headers Reference {#request_headers}
<div class="info-table">
| Header | Description |
|--------|-------------|
| `HX-Boosted` | indicates that the request is via an element using [hx-boost](@/attributes/hx-boost.md)
| `HX-Current-URL` | the current URL of the browser
| `HX-History-Restore-Request` | "true" if the request is for history restoration after a miss in the local history cache
| `HX-Prompt` | the user response to an [hx-prompt](@/attributes/hx-prompt.md)
| `HX-Request` | always "true"
| `HX-Target` | the `id` of the target element if it exists
| `HX-Trigger-Name` | the `name` of the triggered element if it exists
| `HX-Trigger` | the `id` of the triggered element if it exists
</div>
### Response Headers Reference {#response_headers}
<div class="info-table">
| Header | Description |
|------------------------------------------------------|-------------|
| [`HX-Location`](@/headers/hx-location.md) | allows you to do a client-side redirect that does not do a full page reload
| [`HX-Push-Url`](@/headers/hx-push-url.md) | pushes a new url into the history stack
| [`HX-Redirect`](@/headers/hx-redirect.md) | can be used to do a client-side redirect to a new location
| `HX-Refresh` | if set to "true" the client-side will do a full refresh of the page
| [`HX-Replace-Url`](@/headers/hx-replace-url.md) | replaces the current URL in the location bar
| `HX-Reswap` | allows you to specify how the response will be swapped. See [hx-swap](@/attributes/hx-swap.md) for possible values
| `HX-Retarget` | a CSS selector that updates the target of the content update to a different element on the page
| `HX-Reselect` | a CSS selector that allows you to choose which part of the response is used to be swapped in. Overrides an existing [`hx-select`](@/attributes/hx-select.md) on the triggering element
| [`HX-Trigger`](@/headers/hx-trigger.md) | allows you to trigger client-side events
| [`HX-Trigger-After-Settle`](@/headers/hx-trigger.md) | allows you to trigger client-side events after the settle step
| [`HX-Trigger-After-Swap`](@/headers/hx-trigger.md) | allows you to trigger client-side events after the swap step
</div>
## Event Reference {#events}
<div class="info-table">
| Event | Description |
|-------|-------------|
| [`htmx:abort`](@/events.md#htmx:abort) | send this event to an element to abort a request
| [`htmx:afterOnLoad`](@/events.md#htmx:afterOnLoad) | triggered after an AJAX request has completed processing a successful response
| [`htmx:afterProcessNode`](@/events.md#htmx:afterProcessNode) | triggered after htmx has initialized a node
| [`htmx:afterRequest`](@/events.md#htmx:afterRequest) | triggered after an AJAX request has completed
| [`htmx:afterSettle`](@/events.md#htmx:afterSettle) | triggered after the DOM has settled
| [`htmx:afterSwap`](@/events.md#htmx:afterSwap) | triggered after new content has been swapped in
| [`htmx:beforeCleanupElement`](@/events.md#htmx:beforeCleanupElement) | triggered before htmx [disables](@/attributes/hx-disable.md) an element or removes it from the DOM
| [`htmx:beforeOnLoad`](@/events.md#htmx:beforeOnLoad) | triggered before any response processing occurs
| [`htmx:beforeProcessNode`](@/events.md#htmx:beforeProcessNode) | triggered before htmx initializes a node
| [`htmx:beforeRequest`](@/events.md#htmx:beforeRequest) | triggered before an AJAX request is made
| [`htmx:beforeSwap`](@/events.md#htmx:beforeSwap) | triggered before a swap is done, allows you to configure the swap
| [`htmx:beforeSend`](@/events.md#htmx:beforeSend) | triggered just before an ajax request is sent
| [`htmx:beforeTransition`](@/events.md#htmx:beforeTransition) | triggered before the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) wrapped swap occurs
| [`htmx:configRequest`](@/events.md#htmx:configRequest) | triggered before the request, allows you to customize parameters, headers
| [`htmx:confirm`](@/events.md#htmx:confirm) | triggered after a trigger occurs on an element, allows you to cancel (or delay) issuing the AJAX request
| [`htmx:historyCacheError`](@/events.md#htmx:historyCacheError) | triggered on an error during cache writing
| [`htmx:historyCacheMiss`](@/events.md#htmx:historyCacheMiss) | triggered on a cache miss in the history subsystem
| [`htmx:historyCacheMissError`](@/events.md#htmx:historyCacheMissError) | triggered on a unsuccessful remote retrieval
| [`htmx:historyCacheMissLoad`](@/events.md#htmx:historyCacheMissLoad) | triggered on a successful remote retrieval
| [`htmx:historyRestore`](@/events.md#htmx:historyRestore) | triggered when htmx handles a history restoration action
| [`htmx:beforeHistorySave`](@/events.md#htmx:beforeHistorySave) | triggered before content is saved to the history cache
| [`htmx:load`](@/events.md#htmx:load) | triggered when new content is added to the DOM
| [`htmx:noSSESourceError`](@/events.md#htmx:noSSESourceError) | triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined
| [`htmx:onLoadError`](@/events.md#htmx:onLoadError) | triggered when an exception occurs during the onLoad handling in htmx
| [`htmx:oobAfterSwap`](@/events.md#htmx:oobAfterSwap) | triggered after an out of band element as been swapped in
| [`htmx:oobBeforeSwap`](@/events.md#htmx:oobBeforeSwap) | triggered before an out of band element swap is done, allows you to configure the swap
| [`htmx:oobErrorNoTarget`](@/events.md#htmx:oobErrorNoTarget) | triggered when an out of band element does not have a matching ID in the current DOM
| [`htmx:prompt`](@/events.md#htmx:prompt) | triggered after a prompt is shown
| [`htmx:pushedIntoHistory`](@/events.md#htmx:pushedIntoHistory) | triggered after a url is pushed into history
| [`htmx:replacedInHistory`](@/events.md#htmx:replacedInHistory) | triggered after a url is replaced in history
| [`htmx:responseError`](@/events.md#htmx:responseError) | triggered when an HTTP response error (non-`200` or `300` response code) occurs
| [`htmx:sendAbort`](@/events.md#htmx:sendAbort) | triggered when a request is aborted
| [`htmx:sendError`](@/events.md#htmx:sendError) | triggered when a network error prevents an HTTP request from happening
| [`htmx:sseError`](@/events.md#htmx:sseError) | triggered when an error occurs with a SSE source
| [`htmx:sseOpen`](/events#htmx:sseOpen) | triggered when a SSE source is opened
| [`htmx:swapError`](@/events.md#htmx:swapError) | triggered when an error occurs during the swap phase
| [`htmx:targetError`](@/events.md#htmx:targetError) | triggered when an invalid target is specified
| [`htmx:timeout`](@/events.md#htmx:timeout) | triggered when a request timeout occurs
| [`htmx:validation:validate`](@/events.md#htmx:validation:validate) | triggered before an element is validated
| [`htmx:validation:failed`](@/events.md#htmx:validation:failed) | triggered when an element fails validation
| [`htmx:validation:halted`](@/events.md#htmx:validation:halted) | triggered when a request is halted due to validation errors
| [`htmx:xhr:abort`](@/events.md#htmx:xhr:abort) | triggered when an ajax request aborts
| [`htmx:xhr:loadend`](@/events.md#htmx:xhr:loadend) | triggered when an ajax request ends
| [`htmx:xhr:loadstart`](@/events.md#htmx:xhr:loadstart) | triggered when an ajax request starts
| [`htmx:xhr:progress`](@/events.md#htmx:xhr:progress) | triggered periodically during an ajax request that supports progress events
</div>
## JavaScript API Reference {#api}
<div class="info-table">
| Method | Description |
|-------|-------------|
| [`htmx.addClass()`](@/api.md#addClass) | Adds a class to the given element
| [`htmx.ajax()`](@/api.md#ajax) | Issues an htmx-style ajax request
| [`htmx.closest()`](@/api.md#closest) | Finds the closest parent to the given element matching the selector
| [`htmx.config`](@/api.md#config) | A property that holds the current htmx config object
| [`htmx.createEventSource`](@/api.md#createEventSource) | A property holding the function to create SSE EventSource objects for htmx
| [`htmx.createWebSocket`](@/api.md#createWebSocket) | A property holding the function to create WebSocket objects for htmx
| [`htmx.defineExtension()`](@/api.md#defineExtension) | Defines an htmx [extension](https://htmx.org/extensions)
| [`htmx.find()`](@/api.md#find) | Finds a single element matching the selector
| [`htmx.findAll()` `htmx.findAll(elt, selector)`](@/api.md#find) | Finds all elements matching a given selector
| [`htmx.logAll()`](@/api.md#logAll) | Installs a logger that will log all htmx events
| [`htmx.logger`](@/api.md#logger) | A property set to the current logger (default is `null`)
| [`htmx.off()`](@/api.md#off) | Removes an event listener from the given element
| [`htmx.on()`](@/api.md#on) | Creates an event listener on the given element, returning it
| [`htmx.onLoad()`](@/api.md#onLoad) | Adds a callback handler for the `htmx:load` event
| [`htmx.parseInterval()`](@/api.md#parseInterval) | Parses an interval declaration into a millisecond value
| [`htmx.process()`](@/api.md#process) | Processes the given element and its children, hooking up any htmx behavior
| [`htmx.remove()`](@/api.md#remove) | Removes the given element
| [`htmx.removeClass()`](@/api.md#removeClass) | Removes a class from the given element
| [`htmx.removeExtension()`](@/api.md#removeExtension) | Removes an htmx [extension](https://htmx.org/extensions)
| [`htmx.swap()`](@/api.md#swap) | Performs swapping (and settling) of HTML content
| [`htmx.takeClass()`](@/api.md#takeClass) | Takes a class from other elements for the given element
| [`htmx.toggleClass()`](@/api.md#toggleClass) | Toggles a class from the given element
| [`htmx.trigger()`](@/api.md#trigger) | Triggers an event on an element
| [`htmx.values()`](@/api.md#values) | Returns the input values associated with the given element
</div>
## Configuration Reference {#config}
Htmx has some configuration options that can be accessed either programmatically or declaratively. They are
listed below:
<div class="info-table">
| Config Variable | Info |
|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `htmx.config.historyEnabled` | defaults to `true`, really only useful for testing |
| `htmx.config.historyCacheSize` | defaults to 10 |
| `htmx.config.refreshOnHistoryMiss` | defaults to `false`, if set to `true` htmx will issue a full page refresh on history misses rather than use an AJAX request |
| `htmx.config.defaultSwapStyle` | defaults to `innerHTML` |
| `htmx.config.defaultSwapDelay` | defaults to 0 |
| `htmx.config.defaultSettleDelay` | defaults to 20 |
| `htmx.config.includeIndicatorStyles` | defaults to `true` (determines if the indicator styles are loaded) |
| `htmx.config.indicatorClass` | defaults to `htmx-indicator` |
| `htmx.config.requestClass` | defaults to `htmx-request` |
| `htmx.config.addedClass` | defaults to `htmx-added` |
| `htmx.config.settlingClass` | defaults to `htmx-settling` |
| `htmx.config.swappingClass` | defaults to `htmx-swapping` |
| `htmx.config.allowEval` | defaults to `true`, can be used to disable htmx's use of eval for certain features (e.g. trigger filters) |
| `htmx.config.allowScriptTags` | defaults to `true`, determines if htmx will process script tags found in new content |
| `htmx.config.inlineScriptNonce` | defaults to `''`, meaning that no nonce will be added to inline scripts |
| `htmx.config.inlineStyleNonce` | defaults to `''`, meaning that no nonce will be added to inline styles |
| `htmx.config.attributesToSettle` | defaults to `["class", "style", "width", "height"]`, the attributes to settle during the settling phase |
| `htmx.config.wsReconnectDelay` | defaults to `full-jitter` |
| `htmx.config.wsBinaryType` | defaults to `blob`, the [the type of binary data](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) being received over the WebSocket connection |
| `htmx.config.disableSelector` | defaults to `[hx-disable], [data-hx-disable]`, htmx will not process elements with this attribute on it or a parent |
| `htmx.config.disableInheritance` | defaults to `false`. If it is set to `true`, the inheritance of attributes is completely disabled and you can explicitly specify the inheritance with the [hx-inherit](@/attributes/hx-inherit.md) attribute.
| `htmx.config.withCredentials` | defaults to `false`, allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates |
| `htmx.config.timeout` | defaults to 0, the number of milliseconds a request can take before automatically being terminated |
| `htmx.config.scrollBehavior` | defaults to 'instant', the scroll behavior when using the [show](@/attributes/hx-swap.md#scrolling-scroll-show) modifier with `hx-swap`. The allowed values are `instant` (scrolling should happen instantly in a single jump), `smooth` (scrolling should animate smoothly) and `auto` (scroll behavior is determined by the computed value of [scroll-behavior](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior)). |
| `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier. |
| `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will append the target element to the `GET` request in the format `org.htmx.cache-buster=targetElementId` |
| `htmx.config.globalViewTransitions` | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content. |
| `htmx.config.methodsThatUseUrlParams` | defaults to `["get", "delete"]`, htmx will format requests with these methods by encoding their parameters in the URL, not the request body |
| `htmx.config.selfRequestsOnly` | defaults to `true`, whether to only allow AJAX requests to the same domain as the current document |
| `htmx.config.ignoreTitle` | defaults to `false`, if set to `true` htmx will not update the title of the document when a `title` tag is found in new content |
| `htmx.config.scrollIntoViewOnBoost` | defaults to `true`, whether or not the target of a boosted element is scrolled into the viewport. If `hx-target` is omitted on a boosted element, the target defaults to `body`, causing the page to scroll to the top. |
| `htmx.config.triggerSpecsCache` | defaults to `null`, the cache to store evaluated trigger specifications into, improving parsing performance at the cost of more memory usage. You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) |
| `htmx.config.responseHandling` | the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error |
| `htmx.config.allowNestedOobSwaps` | defaults to `true`, whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps). |
</div>
You can set them directly in javascript, or you can use a `meta` tag:
\`\`\`html
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
\`\`\`
```
+++
title = "Documentation"
[extra]
custom_classes = "wide-content"
+++
<div class="row">
<div class="2 col nav">
**Contents**
<div id="contents">
* [introduction](#introduction)
* [installing](#installing)
* [ajax](#ajax)
* [triggers](#triggers)
* [trigger modifiers](#trigger-modifiers)
* [trigger filters](#trigger-filters)
* [special events](#special-events)
* [polling](#polling)
* [load polling](#load_polling)
* [indicators](#indicators)
* [targets](#targets)
* [swapping](#swapping)
* [synchronization](#synchronization)
* [css transitions](#css_transitions)
* [out of band swaps](#oob_swaps)
* [parameters](#parameters)
* [confirming](#confirming)
* [inheritance](#inheritance)
* [boosting](#boosting)
* [websockets & SSE](#websockets-and-sse)
* [history](#history)
* [requests & responses](#requests)
* [validation](#validation)
* [animations](#animations)
* [extensions](#extensions)
* [events & logging](#events)
* [debugging](#debugging)
* [scripting](#scripting)
* [hx-on attribute](#hx-on)
* [3rd party integration](#3rd-party)
* [Web Components](#web-components)
* [caching](#caching)
* [security](#security)
* [configuring](#config)
</div>
</div>
<div class="10 col">
## htmx in a Nutshell {#introduction}
htmx is a library that allows you to access modern browser features directly from HTML, rather than using
javascript.
To understand htmx, first let's take a look at an anchor tag:
```html
<a href="/blog">Blog</a>
```
This anchor tag tells a browser:
> "When a user clicks on this link, issue an HTTP GET request to '/blog' and load the response content
> into the browser window".
With that in mind, consider the following bit of HTML:
```html
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML">
Click Me!
</button>
```
This tells htmx:
> "When a user clicks on this button, issue an HTTP POST request to '/clicked' and use the content from the response
> to replace the element with the id `parent-div` in the DOM"
htmx extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly
within the language:
* Now any element, not just anchors and forms, can issue an HTTP request
* Now any event, not just clicks or form submissions, can trigger requests
* Now any [HTTP verb](https://en.wikipedia.org/wiki/HTTP_Verbs), not just `GET` and `POST`, can be used
* Now any element, not just the entire window, can be the target for update by the request
Note that when you are using htmx, on the server side you typically respond with *HTML*, not *JSON*. This keeps you firmly
within the [original web programming model](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm),
using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)
without even needing to really understand that concept.
It's worth mentioning that, if you prefer, you can use the [`data-`](https://html.spec.whatwg.org/multipage/dom.html#attr-data-*) prefix when using htmx:
```html
<a data-hx-post="/click">Click Me!</a>
```
If you understand the concepts around htmx and want to see the quirks of the library, please see our
[QUIRKS](@/QUIRKS.md) page.
## 1.x to 2.x Migration Guide
[Version 1](https://v1.htmx.org) of htmx is still supported and supports IE11, but the latest version of htmx is 2.x.
If you are migrating to htmx 2.x from [htmx 1.x](https://v1.htmx.org), please see the [htmx 1.x migration guide](@/migration-guide-htmx-1.md).
If you are migrating to htmx from intercooler.js, please see the [intercooler migration guide](@/migration-guide-intercooler.md).
## Installing
Htmx is a dependency-free, browser-oriented javascript library. This means that using it is as simple as adding a `<script>`
tag to your document head. There is no need for a build system to use it.
### Via A CDN (e.g. unpkg.com)
The fastest way to get going with htmx is to load it via a CDN. You can simply add this to
your head tag and get going:
```html
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
```
An unminified version is also available as well:
```html
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.js" integrity="sha384-oeUn82QNXPuVkGCkcrInrS1twIxKhkZiFfr2TdiuObZ3n3yIeMiqcRzkIcguaof1" crossorigin="anonymous"></script>
```
While the CDN approach is extremely simple, you may want to consider
[not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn).
### Download a copy
The next easiest way to install htmx is to simply copy it into your project.
Download `htmx.min.js` [from unpkg.com](https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js) and add it to the appropriate directory in your project
and include it where necessary with a `<script>` tag:
```html
<script src="/path/to/htmx.min.js"></script>
```
### npm
For npm-style build systems, you can install htmx via [npm](https://www.npmjs.com/):
```sh
npm install htmx.org@2.0.4
```
After installing, you’ll need to use appropriate tooling to use `node_modules/htmx.org/dist/htmx.js` (or `.min.js`).
For example, you might bundle htmx with some extensions and project-specific code.
### Webpack
If you are using webpack to manage your javascript:
* Install `htmx` via your favourite package manager (like npm or yarn)
* Add the import to your `index.js`
```js
import 'htmx.org';
```
If you want to use the global `htmx` variable (recommended), you need to inject it to the window scope:
* Create a custom JS file
* Import this file to your `index.js` (below the import from step 2)
```js
import 'path/to/my_custom.js';
```
* Then add this code to the file:
```js
window.htmx = require('htmx.org');
```
* Finally, rebuild your bundle
## AJAX
The core of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML:
| Attribute | Description |
|----------------------------------------|--------------------------------------------|
| [hx-get](@/attributes/hx-get.md) | Issues a `GET` request to the given URL |
| [hx-post](@/attributes/hx-post.md) | Issues a `POST` request to the given URL |
| [hx-put](@/attributes/hx-put.md) | Issues a `PUT` request to the given URL |
| [hx-patch](@/attributes/hx-patch.md) | Issues a `PATCH` request to the given URL |
| [hx-delete](@/attributes/hx-delete.md) | Issues a `DELETE` request to the given URL |
Each of these attributes takes a URL to issue an AJAX request to. The element will issue a request of the specified
type to the given URL when the element is [triggered](#triggers):
```html
<button hx-put="/messages">
Put To Messages
</button>
```
This tells the browser:
> When a user clicks on this button, issue a PUT request to the URL /messages and load the response into the button
### Triggering Requests {#triggers}
By default, AJAX requests are triggered by the "natural" event of an element:
* `input`, `textarea` & `select` are triggered on the `change` event
* `form` is triggered on the `submit` event
* everything else is triggered by the `click` event
If you want different behavior you can use the [hx-trigger](@/attributes/hx-trigger.md)
attribute to specify which event will cause the request.
Here is a `div` that posts to `/mouse_entered` when a mouse enters it:
```html
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
```
#### Trigger Modifiers
A trigger can also have a few additional modifiers that change its behavior. For example, if you want a request to only
happen once, you can use the `once` modifier for the trigger:
```html
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[Here Mouse, Mouse!]
</div>
```
Other modifiers you can use for triggers are:
* `changed` - only issue a request if the value of the element has changed
* `delay:<time interval>` - wait the given amount of time (e.g. `1s`) before
issuing the request. If the event triggers again, the countdown is reset.
* `throttle:<time interval>` - wait the given amount of time (e.g. `1s`) before
issuing the request. Unlike `delay` if a new event occurs before the time limit is hit the event will be discarded,
so the request will trigger at the end of the time period.
* `from:<CSS Selector>` - listen for the event on a different element. This can be used for things like keyboard shortcuts. Note that this CSS selector is not re-evaluated if the page changes.
You can use these attributes to implement many common UX patterns, such as [Active Search](@/examples/active-search.md):
```html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search...">
<div id="search-results"></div>
```
This input will issue a request 500 milliseconds after a key up event if the input has been changed and inserts the results
into the `div` with the id `search-results`.
Multiple triggers can be specified in the [hx-trigger](@/attributes/hx-trigger.md) attribute, separated by commas.
#### Trigger Filters
You may also apply trigger filters by using square brackets after the event name, enclosing a javascript expression that
will be evaluated. If the expression evaluates to `true` the event will trigger, otherwise it will not.
Here is an example that triggers only on a Control-Click of the element
```html
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Control Click Me
</div>
```
Properties like `ctrlKey` will be resolved against the triggering event first, then against the global scope. The
`this` symbol will be set to the current element.
#### Special Events
htmx provides a few special events for use in [hx-trigger](@/attributes/hx-trigger.md):
* `load` - fires once when the element is first loaded
* `revealed` - fires once when an element first scrolls into the viewport
* `intersect` - fires once when an element first intersects the viewport. This supports two additional options:
* `root:<selector>` - a CSS selector of the root element for intersection
* `threshold:<float>` - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
You can also use custom events to trigger requests if you have an advanced use case.
#### Polling
If you want an element to poll the given URL rather than wait for an event, you can use the `every` syntax
with the [`hx-trigger`](@/attributes/hx-trigger.md) attribute:
```html
<div hx-get="/news" hx-trigger="every 2s"></div>
```
This tells htmx
> Every 2 seconds, issue a GET to /news and load the response into the div
If you want to stop polling from a server response you can respond with the HTTP response code [`286`](https://en.wikipedia.org/wiki/86_(term))
and the element will cancel the polling.
#### Load Polling {#load_polling}
Another technique that can be used to achieve polling in htmx is "load polling", where an element specifies
a `load` trigger along with a delay, and replaces itself with the response:
```html
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML">
</div>
```
If the `/messages` end point keeps returning a div set up this way, it will keep "polling" back to the URL every
second.
Load polling can be useful in situations where a poll has an end point at which point the polling terminates, such as
when you are showing the user a [progress bar](@/examples/progress-bar.md).
### Request Indicators {#indicators}
When an AJAX request is issued it is often good to let the user know that something is happening since the browser
will not give them any feedback. You can accomplish this in htmx by using `htmx-indicator` class.
The `htmx-indicator` class is defined so that the opacity of any element with this class is 0 by default, making it invisible
but present in the DOM.
When htmx issues a request, it will put a `htmx-request` class onto an element (either the requesting element or
another element, if specified). The `htmx-request` class will cause a child element with the `htmx-indicator` class
on it to transition to an opacity of 1, showing the indicator.
```html
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
```
Here we have a button. When it is clicked the `htmx-request` class will be added to it, which will reveal the spinner
gif element. (I like [SVG spinners](http://samherbert.net/svg-loaders/) these days.)
While the `htmx-indicator` class uses opacity to hide and show the progress indicator, if you would prefer another mechanism
you can create your own CSS transition like so:
```css
.htmx-indicator{
display:none;
}
.htmx-request .htmx-indicator{
display:inline;
}
.htmx-request.htmx-indicator{
display:inline;
}
```
If you want the `htmx-request` class added to a different element, you can use the [hx-indicator](@/attributes/hx-indicator.md)
attribute with a CSS selector to do so:
```html
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
```
Here we call out the indicator explicitly by id. Note that we could have placed the class on the parent `div` as well
and had the same effect.
You can also add the [`disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled) to
elements for the duration of a request by using the [hx-disabled-elt](@/attributes/hx-disabled-elt.md) attribute.
### Targets
If you want the response to be loaded into a different element other than the one that made the request, you can
use the [hx-target](@/attributes/hx-target.md) attribute, which takes a CSS selector. Looking back at our Live Search example:
```html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search...">
<div id="search-results"></div>
```
You can see that the results from the search are going to be loaded into `div#search-results`, rather than into the
input tag.
#### Extended CSS Selectors {#extended-css-selectors}
`hx-target`, and most attributes that take a CSS selector, support an "extended" CSS syntax:
* You can use the `this` keyword, which indicates that the element that the `hx-target` attribute is on is the target
* The `closest <CSS selector>` syntax will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest)
ancestor element or itself, that matches the given CSS selector.
(e.g. `closest tr` will target the closest table row to the element)
* The `next <CSS selector>` syntax will find the next element in the DOM matching the given CSS selector.
* The `previous <CSS selector>` syntax will find the previous element in the DOM matching the given CSS selector.
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
(e.g `find tr` would target the first child descendant row to the element)
In addition, a CSS selector may be wrapped in `<` and `/>` characters, mimicking the
[query literal](https://hyperscript.org/expressions/query-reference/) syntax of hyperscript.
Relative targets like this can be useful for creating flexible user interfaces without peppering your DOM with lots
of `id` attributes.
### Swapping {#swapping}
htmx offers a few different ways to swap the HTML returned into the DOM. By default, the content replaces the
`innerHTML` of the target element. You can modify this by using the [hx-swap](@/attributes/hx-swap.md) attribute
with any of the following values:
| Name | Description
|------|-------------
| `innerHTML` | the default, puts the content inside the target element
| `outerHTML` | replaces the entire target element with the returned content
| `afterbegin` | prepends the content before the first child inside the target
| `beforebegin` | prepends the content before the target in the target's parent element
| `beforeend` | appends the content after the last child inside the target
| `afterend` | appends the content after the target in the target's parent element
| `delete` | deletes the target element regardless of the response
| `none` | does not append content from response ([Out of Band Swaps](#oob_swaps) and [Response Headers](#response-headers) will still be processed)
#### Morph Swaps {#morphing}
In addition to the standard swap mechanisms above, htmx also supports _morphing_ swaps, via extensions. Morphing swaps
attempt to _merge_ new content into the existing DOM, rather than simply replacing it. They often do a better job
preserving things like focus, video state, etc. by mutating existing nodes in-place during the swap operation, at the
cost of more CPU.
The following extensions are available for morph-style swaps:
* [Idiomorph](/extensions/idiomorph) - A morphing algorithm created by the htmx developers.
* [Morphdom Swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) - Based on the [morphdom](https://github.com/patrick-steele-idem/morphdom),
the original DOM morphing library.
* [Alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) - Based on the [alpine morph](https://alpinejs.dev/plugins/morph) plugin, plays
well with alpine.js
#### View Transitions {#view-transitions}
The new, experimental [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
gives developers a way to create an animated transition between different DOM states. It is still in active development
and is not available in all browsers, but htmx provides a way to work with this new API that falls back to the non-transition
mechanism if the API is not available in a given browser.
You can experiment with this new API using the following approaches:
* Set the `htmx.config.globalViewTransitions` config variable to `true` to use transitions for all swaps
* Use the `transition:true` option in the `hx-swap` attribute
* If an element swap is going to be transitioned due to either of the above configurations, you may catch the
`htmx:beforeTransition` event and call `preventDefault()` on it to cancel the transition.
View Transitions can be configured using CSS, as outlined in [the Chrome documentation for the feature](https://developer.chrome.com/docs/web-platform/view-transitions/#simple-customization).
You can see a view transition example on the [Animation Examples](/examples/animations#view-transitions) page.
#### Swap Options
The [hx-swap](@/attributes/hx-swap.md) attribute supports many options for tuning the swapping behavior of htmx. For
example, by default htmx will swap in the title of a title tag found anywhere in the new content. You can turn this
behavior off by setting the `ignoreTitle` modifier to true:
```html
<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">Like</button>
```
The modifiers available on `hx-swap` are:
| Option | Description |
|---------------|----------------------------------------------------------------------------------------------------------|
| `transition` | `true` or `false`, whether to use the view transition API for this swap |
| `swap` | The swap delay to use (e.g. `100ms`) between when old content is cleared and the new content is inserted |
| `settle` | The settle delay to use (e.g. `100ms`) between when new content is inserted and when it is settled |
| `ignoreTitle` | If set to `true`, any title found in the new content will be ignored and not update the document title |
| `scroll` | `top` or `bottom`, will scroll the target element to its top or bottom |
| `show` | `top` or `bottom`, will scroll the target element's top or bottom into view |
All swap modifiers appear after the swap style is specified, and are colon-separated.
See the [hx-swap](@/attributes/hx-swap.md) documentation for more details on these options.
### Synchronization {#synchronization}
Often you want to coordinate the requests between two elements. For example, you may want a request from one element
to supersede the request of another element, or to wait until the other element's request has finished.
htmx offers a [`hx-sync`](@/attributes/hx-sync.md) attribute to help you accomplish this.
Consider a race condition between a form submission and an individual input's validation request in this HTML:
```html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change">
<button type="submit">Submit</button>
</form>
```
Without using `hx-sync`, filling out the input and immediately submitting the form triggers two parallel requests to
`/validate` and `/store`.
Using `hx-sync="closest form:abort"` on the input will watch for requests on the form and abort the input's request if
a form request is present or starts while the input request is in flight:
```html
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
```
This resolves the synchronization between the two elements in a declarative way.
htmx also supports a programmatic way to cancel requests: you can send the `htmx:abort` event to an element to
cancel any in-flight requests:
```html
<button id="request-button" hx-post="/example">
Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Request
</button>
```
More examples and details can be found on the [`hx-sync` attribute page.](@/attributes/hx-sync.md)
### CSS Transitions {#css_transitions}
htmx makes it easy to use [CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) without
javascript. Consider this HTML content:
```html
<div id="div1">Original Content</div>
```
Imagine this content is replaced by htmx via an ajax request with this new content:
```html
<div id="div1" class="red">New Content</div>
```
Note two things:
* The div has the *same* id in the original and in the new content
* The `red` class has been added to the new content
Given this situation, we can write a CSS transition from the old state to the new state:
```css
.red {
color: red;
transition: all ease-in 1s ;
}
```
When htmx swaps in this new content, it will do so in such a way that the CSS transition will apply to the new content,
giving you a nice, smooth transition to the new state.
So, in summary, all you need to do to use CSS transitions for an element is keep its `id` stable across requests!
You can see the [Animation Examples](@/examples/animations.md) for more details and live demonstrations.
#### Details
To understand how CSS transitions actually work in htmx, you must understand the underlying swap & settle model that htmx uses.
When new content is received from a server, before the content is swapped in, the existing
content of the page is examined for elements that match by the `id` attribute. If a match
is found for an element in the new content, the attributes of the old content are copied
onto the new element before the swap occurs. The new content is then swapped in, but with the
*old* attribute values. Finally, the new attribute values are swapped in, after a "settle" delay
(20ms by default). A little crazy, but this is what allows CSS transitions to work without any javascript by
the developer.
### Out of Band Swaps {#oob_swaps}
If you want to swap content from a response directly into the DOM by using the `id` attribute you can use the
[hx-swap-oob](@/attributes/hx-swap-oob.md) attribute in the *response* html:
```html
<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content
```
In this response, `div#message` would be swapped directly into the matching DOM element, while the additional content
would be swapped into the target in the normal manner.
You can use this technique to "piggy-back" updates on other requests.
#### Troublesome Tables
Table elements can be problematic when combined with out of band swaps, because, by the HTML spec, many can't stand on
their own in the DOM (e.g. `<tr>` or `<td>`).
To avoid this issue you can use a `template` tag to encapsulate these elements:
```html
<template>
<tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>
```
#### Selecting Content To Swap
If you want to select a subset of the response HTML to swap into the target, you can use the [hx-select](@/attributes/hx-select.md)
attribute, which takes a CSS selector and selects the matching elements from the response.
You can also pick out pieces of content for an out-of-band swap by using the [hx-select-oob](@/attributes/hx-select-oob.md)
attribute, which takes a list of element IDs to pick out and swap.
#### Preserving Content During A Swap
If there is content that you wish to be preserved across swaps (e.g. a video player that you wish to remain playing
even if a swap occurs) you can use the [hx-preserve](@/attributes/hx-preserve.md)
attribute on the elements you wish to be preserved.
### Parameters
By default, an element that causes a request will include its value if it has one. If the element is a form it
will include the values of all inputs within it.
As with HTML forms, the `name` attribute of the input is used as the parameter name in the request that htmx sends.
Additionally, if the element causes a non-`GET` request, the values of all the inputs of the nearest enclosing form
will be included.
If you wish to include the values of other elements, you can use the [hx-include](@/attributes/hx-include.md) attribute
with a CSS selector of all the elements whose values you want to include in the request.
If you wish to filter out some parameters you can use the [hx-params](@/attributes/hx-params.md) attribute.
Finally, if you want to programmatically modify the parameters, you can use the [htmx:configRequest](@/events.md#htmx:configRequest)
event.
#### File Upload {#files}
If you wish to upload files via an htmx request, you can set the [hx-encoding](@/attributes/hx-encoding.md) attribute to
`multipart/form-data`. This will use a `FormData` object to submit the request, which will properly include the file
in the request.
Note that depending on your server-side technology, you may have to handle requests with this type of body content very
differently.
Note that htmx fires a `htmx:xhr:progress` event periodically based on the standard `progress` event during upload,
which you can hook into to show the progress of the upload.
See the [examples section](@/examples/_index.md) for more advanced form patterns, including [progress bars](@/examples/file-upload.md) and [error handling](@/examples/file-upload-input.md).
#### Extra Values
You can include extra values in a request using the [hx-vals](@/attributes/hx-vals.md) (name-expression pairs in JSON format) and
[hx-vars](@/attributes/hx-vars.md) attributes (comma-separated name-expression pairs that are dynamically computed).
### Confirming Requests {#confirming}
Often you will want to confirm an action before issuing a request. htmx supports the [`hx-confirm`](@/attributes/hx-confirm.md)
attribute, which allows you to confirm an action using a simple javascript dialog:
```html
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
```
Using events you can implement more sophisticated confirmation dialogs. The [confirm example](@/examples/confirm.md)
shows how to use [sweetalert2](https://sweetalert2.github.io/) library for confirmation of htmx actions.
#### Confirming Requests Using Events
Another option to do confirmation with is via the [`htmx:confirm` event](@/events.md#htmx:confirm). This event
is fired on *every* trigger for a request (not just on elements that have a `hx-confirm` attribute) and can be used
to implement asynchronous confirmation of the request.
Here is an example using [sweet alert](https://sweetalert.js.org/guides/) on any element with a `confirm-with-sweet-alert='true'` attribute on it:
```javascript
document.body.addEventListener('htmx:confirm', function(evt) {
if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
evt.preventDefault();
swal({
title: "Are you sure?",
text: "Are you sure you are sure?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
evt.detail.issueRequest();
}
});
}
});
```
## Attribute Inheritance {#inheritance}
Most attributes in htmx are inherited: they apply to the element they are on as well as any children elements. This
allows you to "hoist" attributes up the DOM to avoid code duplication. Consider the following htmx:
```html
<button hx-delete="/account" hx-confirm="Are you sure?">
Delete My Account
</button>
<button hx-put="/account" hx-confirm="Are you sure?">
Update My Account
</button>
```
Here we have a duplicate `hx-confirm` attribute. We can hoist this attribute to a parent element:
```html
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
```
This `hx-confirm` attribute will now apply to all htmx-powered elements within it.
Sometimes you wish to undo this inheritance. Consider if we had a cancel button to this group, but didn't want it to
be confirmed. We could add an `unset` directive on it like so:
```html
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
<button hx-confirm="unset" hx-get="/">
Cancel
</button>
</div>
```
The top two buttons would then show a confirm dialog, but the bottom cancel button would not.
Inheritance can be disabled on a per-element and per-attribute basis using the
[`hx-disinherit`](@/attributes/hx-disinherit.md) attribute.
If you wish to disable attribute inheritance entirely, you can set the `htmx.config.disableInheritance` configuration
variable to `true`. This will disable inheritance as a default, and allow you to specify inheritance explicitly
with the [`hx-inherit`](@/attributes/hx-inherit.md) attribute.
## Boosting
Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](@/attributes/hx-boost.md) attribute. This
attribute will convert all anchor tags and forms into AJAX requests that, by default, target the body of the page.
Here is an example:
```html
<div hx-boost="true">
<a href="/blog">Blog</a>
</div>
```
The anchor tag in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag.
### Progressive Enhancement {#progressive_enhancement}
A feature of `hx-boost` is that it degrades gracefully if javascript is not enabled: the links and forms continue
to work, they simply don't use ajax requests. This is known as
[Progressive Enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), and it allows
a wider audience to use your site's functionality.
Other htmx patterns can be adapted to achieve progressive enhancement as well, but they will require more thought.
Consider the [active search](@/examples/active-search.md) example. As it is written, it will not degrade gracefully:
someone who does not have javascript enabled will not be able to use this feature. This is done for simplicity’s sake,
to keep the example as brief as possible.
However, you could wrap the htmx-enhanced input in a form element:
```html
<form action="/search" method="POST">
<input class="form-control" type="search"
name="search" placeholder="Begin typing to search users..."
hx-post="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
</form>
```
With this in place, javascript-enabled clients would still get the nice active-search UX, but non-javascript enabled
clients would be able to hit the enter key and still search. Even better, you could add a "Search" button as well.
You would then need to update the form with an `hx-post` that mirrored the `action` attribute, or perhaps use `hx-boost`
on it.
You would need to check on the server side for the `HX-Request` header to differentiate between an htmx-driven and a
regular request, to determine exactly what to render to the client.
Other patterns can be adapted similarly to achieve the progressive enhancement needs of your application.
As you can see, this requires more thought and more work. It also rules some functionality entirely out of bounds.
These tradeoffs must be made by you, the developer, with respect to your projects goals and audience.
[Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) is a concept
closely related to progressive enhancement. Using progressive enhancement techniques such as `hx-boost` will make your
htmx application more accessible to a wide array of users.
htmx-based applications are very similar to normal, non-AJAX driven web applications because htmx is HTML-oriented.
As such, the normal HTML accessibility recommendations apply. For example:
* Use semantic HTML as much as possible (i.e. the right tags for the right things)
* Ensure focus state is clearly visible
* Associate text labels with all form fields
* Maximize the readability of your application with appropriate fonts, contrast, etc.
## Web Sockets & SSE {#websockets-and-sse}
Web Sockets and Server Sent Events (SSE) are supported via extensions. Please see
the [SSE extension](/extensions/sse) and [WebSocket extension](/extensions/ws)
pages to learn more.
## History Support {#history}
Htmx provides a simple mechanism for interacting with the [browser history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API):
If you want a given element to push its request URL into the browser navigation bar and add the current state of the page
to the browser's history, include the [hx-push-url](@/attributes/hx-push-url.md) attribute:
```html
<a hx-get="/blog" hx-push-url="true">Blog</a>
```
When a user clicks on this link, htmx will snapshot the current DOM and store it before it makes a request to /blog.
It then does the swap and pushes a new location onto the history stack.
When a user hits the back button, htmx will retrieve the old content from storage and swap it back into the target,
simulating "going back" to the previous state. If the location is not found in the cache, htmx will make an ajax
request to the given URL, with the header `HX-History-Restore-Request` set to true, and expects back the HTML needed
for the entire page. Alternatively, if the `htmx.config.refreshOnHistoryMiss` config variable is set to true, it will