16 KiB
website | obj |
---|---|
https://htmx.org/ | concept |
htmx
htmx is a library that allows you to access modern browser features directly from HTML, rather than using javascript.
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, not just
GET
andPOST
, 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, using Hypertext As The Engine Of Application State without even needing to really understand that concept.
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. No need for complicated build steps or systems.
Via CDN:
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
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 and add it to the appropriate directory in your project and include it where necessary with a <script>
tag:
<script src="/path/to/htmx.min.js"></script>
You can also add extensions this way, by downloading them from the ext/
directory.
AJAX
The core of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML:
Attribute | Description |
---|---|
hx-get | Issues a GET request to the given URL |
hx-post | Issues a POST request to the given URL |
hx-put | Issues a PUT request to the given URL |
hx-patch | Issues a PATCH request to the given URL |
hx-delete | 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:
<div hx-put="/messages">
Put To Messages
</div>
This tells the browser:
When a user clicks on this div, issue a PUT request to the URL /messages and load the response into the div
Triggering Requests
By default, AJAX requests are triggered by the “natural” event of an element:
input
,textarea
&select
are triggered on thechange
eventform
is triggered on thesubmit
event- everything else is triggered by the
click
event
If you want different behavior you can use the hx-trigger attribute to specify which event will cause the request.
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:
<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 changeddelay:<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. Unlikedelay
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.
You can use these attributes to implement many common UX patterns, such as Active Search:
<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 attribute, separated by commas.
Special Events
htmx provides a few special events for use in hx-trigger:
load
- fires once when the element is first loadedrevealed
- fires once when an element first scrolls into the viewportintersect
- fires once when an element first intersects the viewport. This supports two additional options:root:<selector>
- a CSS selector of the root element for intersectionthreshold:<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
attribute:
<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
and the element will cancel the 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:
<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.
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 attribute, which takes a CSS selector. Looking back at our Live Search example:
<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
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 thehx-target
attribute is on is the target - The
closest <CSS selector>
syntax will find the 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 the given CSS selector. find <CSS selector>
which will find the first child descendant element that matches the given CSS selector. (e.gfind tr
would target the first child descendant row to the element)
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 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 targets parent element |
beforeend |
appends the content after the last child inside the target |
afterend |
appends the content after the target in the targets parent element |
delete |
deletes the target element regardless of the response |
none |
does not append content from response (Out of Band Swaps and Response Headers will still be processed) |
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 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 attribute.
Finally, if you want to programmatically modify the parameters, you can use the htmx:configRequest event.
Extra Values
You can include extra values in a request using the hx-vals (name-expression pairs in JSON format) and hx-vars attributes (comma-separated name-expression pairs that are dynamically computed).
Attribute 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:
<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:
<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:
<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.
Automatic inheritance can be disabled using the hx-disinherit
attribute.
Boosting
Htmx supports “boosting” regular HTML anchors and forms with the hx-boost 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:
<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.
History Support
Htmx provides a simple mechanism for interacting with the browser 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 attribute:
<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.
Request & Responses
Request Headers
htmx includes a number of useful headers in requests:
Header | Description |
---|---|
HX-Request |
will be set to “true” |
HX-Trigger |
will be set to the id of the element that triggered the request |
HX-Trigger-Name |
will be set to the name of the element that triggered the request |
HX-Target |
will be set to the id of the target element |
HX-Prompt |
will be set to the value entered by the user when prompted via hx-prompt |
Response Headers
htmx supports some htmx-specific response headers:
HX-Push
- pushes a new URL into the browser’s address barHX-Redirect
- triggers a client-side redirect to a new locationHX-Location
- triggers a client-side redirect to a new location that acts as a swapHX-Refresh
- if set to “true” the client side will do a full refresh of the pageHX-Trigger
- triggers client side eventsHX-Trigger-After-Swap
- triggers client side events after the swap stepHX-Trigger-After-Settle
- triggers client side events after the settle step
For more on the HX-Trigger
headers, see HX-Trigger
Response Headers.
Submitting a form via htmx has the benefit of no longer needing the Post/Redirect/Get Pattern. After successfully processing a POST request on the server, you don’t need to return a HTTP 302 (Redirect). You can directly return the new HTML fragment.