Back to the roots

Web Development With htmx - High-power Tools for HTML

Web Development With htmx - High-power Tools for HTML

Back to the roots

Web Development With htmx - High-power Tools for HTML


htmx is a small, lightweight library that shifts the focus of web front-end development away from JavaScript and large single-page applications back to the origins of the web. In this article, we’ll take a look at the features and uses of htmx.

With their component-oriented architecture, major SPA frameworks like Angular, React, and Vue are committed to a declarative approach. However, the origins of this type of development lie in HTML. With HTML, you follow a semantic structure of your documents. You describe the structure of the website that users receive with a defined set of elements, each with a specific meaning. htmx takes on this idea and extends the possibilities of HTML.

What is htmx and what problems does it solve?

The htmx website provides a nice overview of the library’s core:

  • Why should only certain elements like a form be able to trigger HTTP requests? With htmx, all HTML elements can trigger HTTP requests, like a heading and h1 element.
  • Why can only certain events such as click and submit trigger requests? The library extends the range of events that can lead to a server request. You can also bind a server request to a scroll event or MouseEnter event.
  • Why should only GET and POST requests be possible? You can map all possible operations with detours like the URL path or the query string. However, the clean solution is using the HTTP methods. GET stands for read requests and POST for create requests. You can modify using either PUT or PATCH and delete using the DELETE method. With htmx, you can access all HTTP methods directly from the HTML structure without additional JavaScript functions.
  • Why should only the complete screen content be replaceable? If you click a link or submit a form, the browser completely replaces the current document with the server's response. htmx gives you much better control over this standard behavior and lets you replace only certain parts of the document.

In summary, this means you can keep your familiar HTML structure and extend it selectively. The focus of htmx is on server communication and state transition animations. You can also implement the standard use case of an SPA, a simple CRUD application, with these. They are significantly more lightweight in the process.

Installation and first steps

You can install and integrate htmx in a variety of ways. These range from loading via a CDN, downloading the source code and delivering it statically via a web server, to using a package manager and a bundler like webpack, Rollup, or Vite.

The easiest way to get started with htmx is to use a CDN. You can include it with an ordinary script tag and refer to unpkg.com. Listing 1 contains a simple example that clearly shows how htmx works.

Listing 1: Hello World in htmx

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/htmx.org@1.9.4"></script>
  </head>
  <body>
    <output></output>
    <button
      hx-get="/hello"
      hx-trigger="click"
      hx-target="output"
      hx-swap="innerHTML"
    >
      say hello
    </button>
  </body>
</html>

This code example shows how to integrate htmx into an HTML document header using a CDN. The core of the example is the button element. The four attributes beginning with hx- tell htmx to send an HTTP request when the button is clicked and insert the response into the document.

Individual attributes in the example include:

  • hx-get: The element triggers a GET request. There are also hx-post, hx-put, hx-delete and a few others. The URL specified here determines the request’s destination.
  • hx-trigger: You can use this attribute to specify which browser event triggers the request. In addition to the event, you can also specify modifiers. With one click, you only send a request to the server when the button is clicked for the first time. If you don’t specify this modifier, htmx triggers a new request with every click.
  • hx-target: The hx-target attribute determines where the server data should be inserted. You can use any valid CSS selector as a value. In the example, you insert the content into the output element selected with the output tag selector. If you precede the selector with a #, it is an ID selector, and a . is a class selector. htmx also allows more complex and nested selectors. You can also use additional modifiers, such as closest, which finds the closest element that the specified selector applies to.
  • hx-swap: This attribute controls how htmx inserts content from the server into the document or the target element. The default value innerHTML inserts the server response into the target element. You can replace the element with the value outerHTML. Other options include inserting it before or after the element. Please note that the target element is deleted when using certain values such as outerHTML or delete. In our example, no further requests are triggered after the first request, as htmx throws a targetError.

For the example to work, you need a web server that delivers the source code and responds to a GET request to the URL /hello with an HTML structure.

htmx works with the standard elements of HTML and relies on special attributes. For this, the library searches the DOM structure for the attributes and evaluates them. For the attributes, htmx registers handler functions that are activated once the events are triggered.

Although this example illustrates the principles of htmx well, it’s a long way from a realistic application. But this will change in the next step.

Loading a list

One of the most important features of a typical CRUD application is displaying an overview list. You can render a list on the server side and deliver it to the browser as static HTML. However, you can also use htmx and first display the frame application and load the actual list dynamically. In our example, we will implement an application for managing books, starting with a list of data records. You will need a web server that delivers the base document and renders a series of table rows when the /books path is requested.

Listing 2: List rendering with htmx

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/htmx.org@1.9.4"></script>
  </head>
  <body>
    <table hx-trigger="load" hx-get="/books" hx-target="tbody">
      <thead>
        <tr>
          <th>Titel</th>
          <th>Autor</th>
          <th>Veröffentlichungsjahr</th>
          <th>Seiten</th>
          <th>Sprache</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td colspan="5">Lade Daten</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

In this example, you take advantage of the fact that every element can trigger a request, even for any event. In this case, the table element is responsible for displaying the book list. The event is the load event and the target is the contained tbody element in the table. When the table is loaded, the browser sends a request to the server and loads the actual content. In the meantime, the browser displays information that the data is being loaded. Once the response from the server is available, htmx replaces content from the tbody element with the response from the server.

Handling errors with htmx

If everything goes according to plan, you’ll briefly see the loading information and after just a few moments, the finished table with the data records. But what happens if an error happens on the server side? What if, for instance, the server does not respond with a status code 200 and the desired content, but with an internal server error? In this case, you also want to replace the load information with an error message after receiving the response. htmx focuses on the success case, which is why it’s conveniently implemented. For error handling, you can fall back on several approaches, two of which I’d like to present: handling the htmx error event and an extension for dealing with errors.

htmx error events

htmx can trigger a whole range of events during an application’s runtime. These range...