Drupal developers always face the dilemma of building classic multi-page applications or headless solutions with a modern JavaScript stack. Especially when they need to build UIs that feel fast and are highly reactive. While there were some Drupal specific solutions for parts of this need (Form State API, AJAX API and BigPipe), these were dated, only solving very specific use cases and comparatively heavy in implementation.
HTMX is a tiny, dependency-free, and extensible JavaScript library that allows you to access modern browser features directly from HTML, rather than using extensive amounts of JavaScript. It essentially enables you to use HTML to make AJAX requests, CSS transitions, WebSockets, and Server-Sent Events (SSE) directly.
As a replacement for Drupal's mostly home grown solutions, this reduced the loaded JavaScript size by up to 71% for browser-server interactions, including HTML streaming with BigPipe. While enabling a whole set of new functionality at the same time!

HTMX was originally created by Carson Gross. The motivation was to provide a modern, yet simple, way to build dynamic user interfaces by leveraging the existing capabilities of HTML and the server-side architecture, effectively offering an alternative to the complexity of heavy, client-side JavaScript frameworks. By sending less data (HTML fragments instead of large JSON payloads and complex client-side rendering logic), HTMX often results in faster perceived performance and less bandwidth consumption. It is being adopted by developers across diverse ecosystems.
Principles of HTMX
HTMX operates on a few core, simple principles, all expressed via HTML attributes. While pure HTMX does not require the data- prefix, Drupal uses it to achieve valid HTML. That is how you'll see it used in Drupal, so we'll use that notation in this post.
- Any Element Can Make a Request: Unlike standard HTML forms and anchors, HTMX allows any element (a
<div>, a <span>, a <button>) to trigger an HTTP request. All five HTTP verbs are available.
- Attributes:
data-hx-get, data-hx-post, data-hx-put, data-hx-delete, data-hx-patch.
- Any Event Can Trigger a Request: You are not limited to
click (for anchors/buttons) or submit (for forms). Requests can be triggered by any JavaScript event, such as mouseover, keyup, or a custom event.
- Attribute:
data-hx-trigger.
- Any Target Can Be Updated: By default, HTMX replaces the inner HTML of the element that triggered the request. However, you can use a CSS selector to specify any element on the page to be updated with the response HTML.
- Attribute:
data-hx-target.
- Any Transition Can Be Used: HTMX allows you to define how the new content is swapped into the target element (e.g., replace, prepend, append, outerHTML) and works with the new View Transition API.
Short Code Example
This Drupal-independent example demonstrates how to fetch and swap new content into a div when a button is clicked, without writing any custom JavaScript.
<!-- The button that triggers the request -->
<button data-hx-get="/clicked"
data-hx-target="#content-area"
data-hx-swap="outerHTML">
Load New Content
</button>
<!-- The area that will be updated -->
<div id="content-area">
This content will be replaced.
</div>
In this code example, when the button is clicked:
- A GET request is made to the server at the URL:
/clicked
- The server responds with a fragment of HTML (e.g.,
<div>New Content Loaded!</div>)
- The HTML content of the
#content-area element is replaced (outerHTML) by the response.
Introducing HTMX in Drupal 11.3.0
HTMX was added as a dependency to Drupal core in 11.2, but is now fully featured in 11.3. A new factory class is provided for developers building or extending render arrays. The Htmx class provides methods that build every HTMX attribute or response header, therefore documenting and exposing the HTMX API to Drupal.
Drupal 11.3 also extends the FormBuilder class to support dynamic forms built with HTMX. When a form is rebuilt from an HTMX request, all the form values will be available to the form class for dynamically restructuring the form. Here's an example of both features:
function buildForm(array $form, FormStateInterface $form_state) {
$make = ['Audi', 'Toyota', 'BMW'];
$models = [
['A1', 'A4', 'A6'],
['Landcruiser', 'Tacoma', 'Yaris'],
['325i', '325ix', 'X5'],
];
$form['make'] = [
'#title' => 'Make',
'#type' => 'select',
'#options' => $make,
];
$form['model'] = [
'#title' => 'Models',
'#type' => 'select',
'#options' => $models[$form_state->getValue('make', 0)] ?? [],
// We'll need that later.
'#wrapper_attributes' => ['id' => 'models-wrapper'],
];
return $form;
}
(new Htmx())
// An empty method call uses the current URL.
->post()
// We select the wrapper around the select.
->target('#models-wrapper')
// And replace the whole wrapper
// not simply updating the options in place,
// so that any errors also display.
->select('#models-wrapper')
// We replace the whole element for this form.
->swap('outerHTML')
->applyTo($form['make']);
In this form, whenever the make selector is changed, the models selector will be updated.
Drupal 11.3 also adds a dedicated renderer and associated wrapper format that can be used to keep the response to an HTMX request as small as possible. This render only returns the main content and its CSS/Javascript assets. There are two ways to take advantage of this renderer.
One is to add an attribute to the HTMX enhanced element, which will cause the wrapper format to be used:
(new Htmx())
->post()
->onlyMainContent()
->target('#models-wrapper')
->select('#models-wrapper')
->swap('outerHTML')
->applyTo($form['make']);
There is also a new route option that can be used when creating a route specifically to service HTMX requests. This route option will also be useful with the dynamic routes in Views as we refactor to use HTMX.
demo.route_option:
path: '/htmx-demo/route-option'
defaults:
_title: 'Using _htmx_route option'
_controller: '\Drupal\module_name\Controller\DemoController::replace'
requirements:
_permission: 'access content'
options:
_htmx_route: TRUE
Drupal is still committed to supporting decoupled architectures
HTMX is an excellent solution for progressive enhancement and dynamic front-ends. It is a powerful tool in the core toolkit, not a replacement for the flexibility offered by a fully decoupled backend. Drupal remains committed to supporting decoupled and headless architectures, especially where necessary, such as mobile applications, client-side state management, deep offline capabilities, etc.