HTMX - Exploring The Capabilities
Tags: react, html, javascript, webdev
Published: 2023-11-22 18:34:40

<a href="https://github.com/refinedev/refine">
   <img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog-banners/retool-alternative.png" alt="refine repo" /> 
</div>

---

**Author: <a target="_blank" href="https://refine.dev/blog/author/peter_osah/">Peter O.</a>**



## Introduction

In this article, we will explore what HTMX is and its capabilities.

HTMX is a small (14k min. gzipped), dependency-free javascript library that can create cutting-edge user interfaces with the ease and power of hypertext (markup).
It provides access to AJAX, CSS Transitions, WebSockets, and Server Sent Events directly in HTML using attributes. This has become a game-changer for developers as it enables them to achieve interactivity (that JavaScript gives) from the markup alone.

Steps to follow in this article:

- [Validation with HTMX](#validation-with-htmx)
  - [Animations with HTMX](#animations-with-htmx)
  - [Extensions in HTMX](#extensions-in-htmx)
  - [Logging and events in HTMX](#logging-and-events-in-htmx)
    - [Events](#events)
  - [Logging](#logging)

## Installing HTMX

You can install HTMX using three methods:

**Through A CDN**: You can load HTMX to your website/application by including the CDN at the head of your HTML file.

```tsx
<script
  src="https://unpkg.com/htmx.org@1.9.6"
  integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
  crossorigin="anonymous"
></script>
```

N/B: While this is possible,it is not advisable to use CDNs in production environments.

**You can download a copy of the HTMX source file intoyour website/application**

Download `htmx.min.js` from [unpkg.com](https://unpkg.com/htmx.org/dist/htmx.min.js), place it in the proper directory in your project, then include it with a script> tag as needed:

```tsx
<script src="/path/to/htmx.min.js"></script>
```

**Installing through npm**: you can install HTMX through npm using the below command

```bash
npm install htmx.org
```

After installing, you'll need to use the proper tooling to utilize `node_modules/htmx.org/dist/htmx.js` or `node_modules/htmx.org/dist/htmx.min.js)`.
For instance, you may package HTMX with certain extensions and project-specific code.

**Setting up HTMX in Webpack**: If you are using webpack to manage your javascript, firstly, Install htmx via your package manager (**npm**, **yarn**, **pnpm**) and then add the import to your `index.js`

```tsx
import "htmx.org";
```

If you want to use the global htmx variable, you can inject it into the window scope as follows:

- Create a unique JS file.
- Import this file into your `index.js` file.

```tsx
import "path/to/my_custom.js";
```

- Then add the code below to the file

```tsx
window.htmx = require("htmx.org");
```

- Finally, rebuild your application bundle(to reflect the changes)

## Ajax Requests with HTMX

HTMX provides custom attributes that allow you to perform AJAX requests directly from the HTML:

| Attribute | Description                                |
| --------- | ------------------------------------------ |
| hx-post   | fires a post request to a given url        |
| hx-get    | fires a get request request to a given url |
| hx-put    | fires a put request to a given url         |
| hx-patch  | fires a patch request to a given url       |
| hx-delete | fires a delete request to a given url      |

#### Use case

```tsx
<div hx-post="/messages">Put To Messages</div>
```

from the code above the `hx-post` attribute triggers a post api call to the url `"/messages"`

## Trigger Request

In HTMX, an element's natural event initiates AJAX requests. For instance, the **onchange** event triggers the `input`, `select`, and `textarea`; the **onsubmit** event triggers the `form`; and the **onclick** event triggers everything else.

However, HTMX offers a unique **hx-trigger** attribute for situations when you want to change the event that initiates the request:

```tsx
<div hx-post="http://localhost:3000/submit" hx-trigger="mouseenter">
  Submit
</div>
```

In the code above, only if the user's mouse hovers above the div will the GET request be made to the specified URL.

## Trigger Modifiers

A trigger's behavior can also be altered by a few other modifications. Use the once modifier for the trigger, for instance, if you want a request to occur only once:

```tsx
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
  Submit
</div>
```

Other trigger modifiers you can use are:
**changed** - Only send a request if the element's value has changed

**delay:`<time interval>`** - It specifies a delay period before making the request. The countdown is reset if the event occurs again.

**throttle:`<interval time>`** - it specifies a delay time before making the request,. In contrast to delay, if a new event happens before the time limit is reached, the event is deleted, and the request is triggered at the end of the time period.

**from:`<CSS Selector>`**- listen to the event of a different element.

to view more modifiers that HTMX supports, visit [here](https://htmx.org/docs/#trigger-modifiers)

## Trigger Filters

These are applied by surrounding a javascript expression in square brackets after the event name. If the expression evaluates to true, the event will be triggered; otherwise, it will not.

```tsx
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
  Click the force
</div>
```

htmx includes a few special events that can be used with **hx-trigger**:

- **load** - fires once when the element is loaded for the first time.
- **revealed** - fires when an element first scrolls into the viewport
- **intersect** - fires when an element first intersects the viewport. This allows for two additional options:
  - **root:`<selector>`** - a root element CSS selector for intersection
  - **threshold:`<float>`** - a floating point number between 0.0 and 1.0 indicating the amount of intersection on which the event should be triggered.

If you have a more advanced use case, you may also utilize custom events to trigger requests.

## Polling

you can use the `every` syntax with a timeframe (in seconds) with the **hx-trigger** property to make an element poll a specific URL:

```tsx
<div hx-get="/get-star-wars-data" hx-trigger="every 2s"></div>
```

## Load Polling

"Load polling" is an additional method for doing polling in HTMLX. It involves an element specifying a load trigger and a delay, and then replacing itself with the response:

```tsx
<div hx-get="/get-star-wars-data" hx-trigger="load delay:1s" hx-swap="starWarsDataHTML"></div>
```

## Request Indicators

Because the browser does not provide feedback when an AJAX request is made, it is often beneficial to notify the user that anything is happening. You may do this with HTMX by using the **htmx-indicator** class.

The opacity of any element with the **htmx-indicator** class is set to **0** by default, meaning that even though the element is present in the DOM, it is invisible.

A **htmx-request** class is added to an element when HTMX submits a request. When a child element with the **htmx-indicator** class on it is exposed to the **htmx-request** class, it will transition to an opacity of **1**, displaying the indicator.

```tsx
<button hx-get="/click">
    Click Me!
    <img class="htmx-indicator" src="/spinner.gif">
</button>
```

Using the **hx-indicator** attribute and a CSS selector, you can add the **htmx-request** class to an alternative element if you wish:

```tsx
<div>
  <button hx-get="/click" hx-indicator="#indicator">
    Click the force!
  </button>
  <img id="indicator" class="htmx-indicator" src="/spinner.gif" />
</div>
```

## Targets

You can load the response into an element other than the one that initiated the request using the **hx-target** attribute which accepts a CSS selector(a div, class, etc).

```tsx
<input type="text" name="star-wars-input"
    hx-get="/star-wars-characters"
    hx-trigger="keyup delay:500ms changed"
    hx-target="#search-results"
    placeholder="Search..."
>
<div id="search-results"></div>
```

In the example above, the **hx-target** attribute targets the `#search-result` id and populate request values to the div containing the id when the request is successful.

## Swapping

You can swap HTML contents returned to the DOM in HTMX. The target element's `innerHTML` is automatically replaced with the content. This can be changed by applying any of the following values to the **hx-swap **attribute:

| Name        | Description                                                            |
| ----------- | ---------------------------------------------------------------------- |
| innerHTML   | the content is inserted inside the target element by default.          |
| outerHTML   | the returning content completely replaces the target element.          |
| afterbegin  | content is prepended before the target's first child                   |
| beforebegin | prepends the content of the target's parent element before the target. |
| beforeend   | adds the content after the target's final child.                       |
| afterend    | adds the content to the targets parent element after the target        |
| delete      | removes the target element irrespective of the response                |
| none        | does not include the response's content                                |

an example of **hx-swap** in action

```tsx
<div hx-get="/example" hx-swap="afterend">
  Get Some HTML & Append It
</div>
```

In the code above, the `div` will make a request and append the returned contents behind the `div`.

## Synchronization

When you make requests in two elements and you want one of them to take priority over the other or wait until the other element's request is fulfilled, HTMX has a **hx-sync** feature that can be helpful.

For example, let us paint a scenario of a race situation between a form submission and a validation request for a specific input:

```tsx
<form hx-post="/submit-character">
    <input id="title" name="title" type="text"
        hx-post="/validate-character"
        hx-trigger="change"
    >
    <button type="submit">Submit</button>
</form>
```

When the input is completed and the form is instantly submitted, two concurrent requests are sent to `/validate-character` and `/submit-character` without the use of **hx-sync**.

When **hx-sync="closest form:abort"** is applied to an input, the form will be watched for requests and the input request will be aborted if a form request is found or begins while the input request is being processed. This automatically solves the synchronization constraint.

```tsx
<form hx-post="/submit-character">
    <input id="title" name="title" type="text"
        hx-post="/validate-character"
        hx-trigger="change"
        hx-sync="closest form:abort"
    >
    <button type="submit">Submit</button>
</form>
```

Additionally, htmx offers a programmatic method for cancelling requests: To stop any in-flight requests, you can send an element the **htmx:abort** event into a **htmx.trigger** function:

```tsx
<button id="request-button" hx-post="/submit-character">
    Submit Character
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
    Cancel Submission
</button>
```

## CSS Transitions

HTMX lets you perform CSS Transitions without the need for javascript. we will illustrate this wwith an example:

```tsx
<div id="div1">The Empire</div>
```

We then make an ajax request and replace the content with the content below:

```tsx
<div id="div1" class="red">
  The Republic
</div>
```

From the code above, a new class `red` has been added to the new content. We can create a CSS transition from the previous state(without the class `red`) to the current one (with the class `red`) in this scenario:

```css
.red {
  color: red;
  transition: all ease-in 1s;
}
```

**How HTMX handles transitions under the hood**:
Before new content is added to a page, elements that match the `id` attribute of the current content are found. This happens when new content is received from a server. Before the swap takes place, the properties of the old content are replicated onto the new element if a match is found for it in the updated content. After that, the new content is replaced, but the old attribute values remain. The new attribute values are then switched in following a "**settle**" time (20 ms by default).

## Out of Band Swaps

The **hx-swap-oob** attribute in the response html can be used to swap content from a response directly into the DOM using the `id` attribute:

```tsx
<div id="message" hx-swap-oob="true">Dark Sidious</div>
Anakin Skywalker
```

from the code above, The extra content would be swapped into the target in the typical way, but `div#message` would be swapped straight into the corresponding DOM element in this response.

## Parameters

You can filter the arguments that will be submitted with an AJAX call using the **hx-params** attribute.

These are the possible values for this attribute:

- "\*"- Include every parameter by default
- **none** - Do not include

- **`<param-list>`** and do not include any parameters. All except the parameter names list

- **`<param-list>`**, which should be comma-separated Provide a list of all the parameter names, separated by commas.

```tsx
<div hx-get="/get-starwars-characters" hx-params="*">
  Luke Skywalker
</div>
```

In the example above, All the parameters that a **POST** would contain are contained in this div, however as is customary with a **GET**, they will be URL encoded and included in the URL.

## Confirming Requests

HTMX provides the **hx-confirm attribute**, which enables you to confirm an action using a straightforward javascript dialogue:

```tsx
<button hx-delete="/delete-character" hx-confirm="Are you sure you wish to delete Obiwan Kenobi">
  Delete Obiwan Kenobi
</button>
```

## Web Sockets & SSE

HTMX currently has experimental supports for WebSockets and Server Sent Events. However, if you want to establish a WebSocket connection, you can use the **hx-ws attribute**

```tsx
<div hx-ws="connect:wss:/darth-chatroom">
    <div id="chat_room">
        ...
    </div>
    <form hx-ws="send:submit">
        <input name="chat_message">
    </form>
</div>
```

To send events to browsers using SSE(Server sent Events), add a connect `<url>` declaration to a parent element and add the **hx-sse** attribute with the URL.

After which, you will use the **`hx-trigger="sse:<event_name>`** attribute to define elements that are this element's descendants and are activated by server-sent events. grammar

A sample is shown below

```tsx
<body hx-sse="connect:/darth-events">
  <div hx-trigger="sse:new_news" hx-get="/news"></div>
</body>
```

## History Support

With the **hx-push-url** attribute on an element you can push a request URL into the browser's navigation bar and add the page's current state to the history:

```tsx
<a hx-get="/star-wars-characters" hx-push-url="true">
  Blog
</a>
```

**Principle on how the hx-push-url works**:
Before making a request to `/star-wars-characters`, HTMX will take a snapshot of the current document and store it when a user clicks on this link. After that, a new location is added to the history stack and the swap is completed.

HTMX simulates "going back" to the prior state when a user presses the back button by retrieving the old content from storage and swapping it back into the target. HTMX will send an ajax call to the specified URL with the header `HX-History-Restore-call` set to `true` if the location cannot be located in the cache, and it will expect back the HTML required for the full page. Alternatively, it will force a hard browser refresh if the `htmx.config.refreshOnHistoryMiss` config setting is set to true.

## Validation with HTMX

Because HTMX works with the HTML5 Validation API, an invalid validatable input will prevent a form request from being made. This applies to both WebSocket sends and AJAX queries.

Events related to validation in HTMX are:

**htmx:validation:validate** - called before an elements 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

```tsx
<form hx-post="/create-characters">
    <input _="on htmx:validation:validate
                if my.value != 'foo'
                    call me.setCustomValidity('Please enter the value foo')
                else
                    call me.setCustomValidity('')"
        name="example"
    >
</form>
```

The code above is an illustration of an input using the htmx:validation. In the code, [hyperscript](https://hyperscript.org/) was used to validate the event to ensure that an input have the value `foo`.

## Animations with HTMX

HTMX is intended to allow you to add seamless animations and transitions to your web page using only CSS and HTML. You can now utilize the new [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) to create animations with HTMX.
A basic example is the **Fade Out On Swap** animation.
If you want to fade out an element that will be removed when the request is finished, use the **htmx-swapping** class with some CSS and extend the swap phase to be long enough for your animation to finish. This can be accomplished as follows:

```css
<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>
```

more examples of animations with HTMX are shown [here](https://htmx.org/examples/animations/)

## Extensions in HTMX

HTMX includes an extension framework for creating and deploying extensions within htmx-based applications.
With the **hx-ext** attribute You can utilize extension libraries(after being specified in javascript).

Using an extension entails two steps:

- Include the extension definition, which will add it to the HTMX extension registry.
- The **hx-ext** attribute is used to specify the extension.

An example is shown below:

```tsx
// Including the extension library
<script src="/path/to/ext/debug.js" defer></script>

// add the extension(debug) to the hx-ext attribute
<button hx-post="/example" hx-ext="debug">
    This Button Uses The Force
</button>
```

HTMX comes with a set of extensions that cover common developer needs. In each release, these extensions are tested against htmx. In order to view the list of included extensions in HTMX, visit [here](https://htmx.org/extensions/#reference)

## Logging and events in HTMX

### Events

HTMX includes a robust events mechanism that also serves as the logging system.

You can easily register to a HTMX event through:
**The event listener approach**:

```tsx
document.body.addEventListener("htmx:load", function (evt) {
  myJavascriptLib.init(evt.detail.elt);
});
```

**HTMX helpers**:

```tsx
htmx.on("htmx:load", function (evt) {
  myJavascriptLib.init(evt.detail.elt);
});
```

in both cases, `htmx:load` is the event.

The `htmx:load` event is fired whenever HTMX loads an element into the DOM, and it is functionally the same as the conventional load event.

You can also initialize a third-party library Using the `htmx:load` event:

```tsx
htmx.onLoad(function (target) {
  myJavascriptLib.init(target);
});
```

You can also configure a request with events. An example of this is shown below:

```tsx
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
});
```

In the code above, we used the `htmx:configRequest` event to change an AJAX request before it is sent:

You can adjust the swap behavior of HTMX by handling the `htmx:beforeSwap` event.

```tsx
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");
  }
});
```

In the code above, we handled a few `400`-level error response codes that would ordinarily not result in a swap in htmx.

In order to view more events that HTMX supports, visit [here](https://htmx.org/reference/#events).

## Logging

HTMX provides a `htmx.logger` variable which when set, logs every event. An example on how to set the logger is shown below:

```tsx
htmx.logger = function (elt, event, data) {
  if (console) {
    console.log(event, elt, data);
  }
};
```

## Conclusion

In this article, we provided an overview of the HTMX library. we showed that you can achieve Ajax functionality without relying on JavaScript with HTMX, allowing you to construct dynamic applications at ease.
HTMX may be more than just a new library. It may revolutionize the way we approach interactivity in web development.