---
title: Introduction to HTMX
description: We will explore what HTMX is and its capabilities.
slug: what-is-htmx
authors: peter_osah
tags: [dev-tools]
image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-10-26-htmx/social-3.png
hide_table_of_contents: false
---

**This article was last updated on August 21, 2024, to add sections on State Management with HTMX and Error Handling with HTMX.**

## 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 with 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`.

## State Management Using HTMX

I'll just share some thoughts on how the whole state management thing works in HTMX, and how this might compare with how you've been doing it with traditional JavaScript frameworks like React and Vue.

### 1. **Server-Driven State**

HTMX is state management with a server-driven approach. It means most of the logic and state handling are done on the server rather than on the client. This contrasts sharply with JavaScript frameworks like React that handle state on the client side. Each and every action or interaction in HTMX (like form submissions and button clicks) creates a request that's made to a server. In response, it receives HTML back from the server to update the client-side DOM without any further effort on your part.

For example:

```html
<div hx-get="/get-data" hx-trigger="click" hx-swap="outerHTML">
  Click to Get Data
</div>
```

Here, the state is maintained on the server, and HTMX will update the DOM according to the response from the server to keep the client in sync with the latest state.

### 2. **Client-Side State Management Not Possible**

Unlike React, where state is managed using `useState` and `useReducer`, HTMX relies on very little client-side state. Then, if you are in a server-centric world—which is quite good if your application is not one of these super-client interactive applications—the state management is easier, and you do not have to have all these huge, heavy client-side tools managing the state for you.

### 3. **Forms and Inputs Handling**

For forms, HTMX supports even form submission in an asynchronous fashion, whereby even the state of the form can be handled by directly listening on the server's responses. This eliminates all the boilerplate of tracking client-side state: the server is responsible for validation, processing, and changing state and then updating the UI accordingly.

Sample:

```html
<form hx-post="/submit-form" hx-swap="outerHTML">
  <input name="username" type="text" />
  <button type="submit">Submit</button>
</form>
```

In this case, the server is in control of the form submission, and it directly sends the server response back to the DOM, updating it accordingly.

### 4. **Persistence Across Requests**

Because HTMX is essentially server-driven, it can be used most productively with server-side session state or database-driven state that can be preserved across requests. This makes handling things like persistent user sessions, authentication, and long-running operations easy without the need to store state in the browser or client.

### 5. **Dealing with Complex State**

If your app needs more complex state management—like multiple interacting components or maintaining state across different elements...

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

## Error Handling in HTMX

I have a few thoughts about how error handling works in HTMX and how we could manage errors gracefully in our applications.

### 1. **Simple Error Handling**

HTMX has built-in events that can help us catch Ajax request errors or form submission errors in a custom manner. With an error like 404, 500, or a validation failure, we get an opportunity to intercept the error using the event `htmx:responseError`.

Here's how you would handle a trivial error event:

```javascript
document.body.addEventListener("htmx:responseError", function (evt) {
  alert("An error occurred: " + evt.detail.xhr.status);
});
```

This event is triggered every time an AJAX request fails, and you can specify the response based on the HTTP status code, such as 404 or 500.

### 2. **Presenting Error Messages to Users**

Provide friendly UI error messages on page failure, rather than a pure alert or default browser error, by listening for the event `htmx:beforeSwap` and injecting error content into an area of the page you designate:

```javascript
document.body.addEventListener("htmx:beforeSwap", function (evt) {
  if (evt.detail.xhr.status >= 400) {
    evt.detail.shouldSwap = false;
    document.getElementById("error-message").innerHTML =
      "Something went wrong. Please try again.";
  }
});
```

In this case, an error will prevent a default content swap and instead show the content of a custom error message in a `#error-message` div.

### 3. **Handling Form Validation Errors**

With HTMX, form validation is very easy to handle. For example, if the server returns an error about validation (e.g., 422 Unprocessable Entity), we can easily manage such a response and show validation messages without page reloading.

Here's an example:

```html
<form hx-post="/submit" hx-target="#form-errors" hx-swap="outerHTML">
  <input name="username" type="text" />
  <button type="submit">Submit</button>
</form>

<div id="form-errors"></div>
```

If the server returns validation errors, those can be added to the response and will be shown inside the `#form-errors` without a page reload.

### 4. **Graceful Fallback for Non-HTMX Browsers**

One thing HTMX is really good at is being able to progressively enhance your application, but you need to handle errors gracefully for your non-HTMX users—that is, those with JavaScript disabled. In that case, we return a full-page reload in case of necessity, so the classic form submission and error handling still work.

### 5. **Request-Specific Error Handling**

HTMX allows you to work with errors on a per-request basis by attaching the error handling logic to specific elements. For instance, if there is a button that triggers an AJAX request, you could listen for events like:

```javascript
document
  .getElementById("my-button")
  .addEventListener("htmx:responseError", function (evt) {
    console.log("Error on this button request: " + evt.detail.xhr.status);
  });
```

This allows granular error handling, where it is easy to distinguish the error responses for different parts of an application.

### 6. **Handling Different HTTP Error Codes**

HTMX does have some flexibility in how you can treat various HTTP error codes. For example, you might want to treat a `404` error differently from a `500` error or handle a `403` Forbidden by redirecting users to a login page:

```javascript
document.body.addEventListener("htmx:beforeSwap", function (evt) {
  if (evt.detail.xhr.status === 404) {
    alert("Page not found.");
    evt.detail.shouldSwap = false;
  } else if (evt.detail.xhr.status === 403) {
    window.location.href = "/login";
  }
});
```

For instance, we show an alert for `404` errors, and in case a `403` Forbidden error occurs, the user is redirected to the login page.

### 7. **Programmatically Aborting Requests**

Sometimes, you would want to programmatically cancel ongoing requests when a new one is triggered. HTMX provides the `htmx:abort` event for this:

```html
<button id="submit-btn" hx-post="/submit" hx-target="#result">Submit</button>
<button onclick="htmx.trigger('#submit-btn', 'htmx:abort')">
  Cancel Request
</button>
```

This will cancel all requests made by the `Submit` button.

## 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.
