Skip to main content
Home
Drupal life hacks

Main navigation

  • Drupal
  • React
  • WP
  • Contact
  • About
User account menu
  • Log in

Breadcrumb

  1. Home

Native HTMX in Drupal 11.3: Rich UX with Less JavaScript

By admin, 20 December, 2025
Native HTMX in Drupal 11.3: Rich UX with Less JavaScript

Drupal has long offered multiple ways to build dynamic user interfaces: Form API AJAX, BigPipe, and fully decoupled frontends with React or Vue. Each approach solved specific problems, but often at the cost of complexity, heavy JavaScript, or difficult maintenance.

With Drupal 11.3, HTMX becomes a first‑class citizen in core, offering a modern alternative: rich, reactive UX built primarily with HTML and server-side rendering, while dramatically reducing JavaScript overhead.

In this article, we’ll look at: - What HTMX is and why it matters for Drupal - What exactly landed in Drupal 11.3 - Practical examples with render arrays, forms, and routes - How HTMX works together with BigPipe


What Is HTMX?

HTMX is a small, dependency-free JavaScript library that allows HTML elements to make HTTP requests and update the DOM using HTML attributes instead of custom JavaScript.

Instead of writing JS like: - event listeners - fetch / XMLHttpRequest - DOM replacement logic

You describe interactions declaratively in HTML.

In Drupal, HTMX attributes are exposed using the data-hx-* syntax to ensure valid HTML.


Core HTMX Concepts

1. Any Element Can Make a Request

HTMX is not limited to <a> or <form> elements. Any element can trigger a request using any HTTP verb.

<button data-hx-get="/demo/replace">Load content</button>

Supported attributes:

  •  - data-hx-get
  • - data-hx-post
  • - data-hx-put
  • - data-hx-patch
  • - data-hx-delete

2. Any Event Can Trigger a Request

Requests are not limited to click or submit.

<input
  data-hx-get="/search"
  data-hx-trigger="keyup changed delay:300ms"
  data-hx-target="#results"
/>

3. Any Target Can Be Updated

By default, HTMX swaps content into the element that triggered the request. You can explicitly define a target using a CSS selector.

<div id="results"></div>
<button
  data-hx-get="/demo/replace"
  data-hx-target="#results">
  Update results
</button>

4. Flexible Swap Strategies

HTMX controls how new HTML is inserted.

data-hx-swap="innerHTML | outerHTML | append | prepend"

In Drupal + BigPipe scenarios, outerHTML is usually the safest choice.


HTMX in Drupal 11.3

HTMX was added as a dependency in Drupal 11.2, but Drupal 11.3 introduces full framework-level support.

Key additions:

  • Htmx factory class for render arrays
  • Native support for HTMX-powered Form API rebuilds
  • Optimized HTMX responses (main content only)
  • _htmx_route option for routes

Using the Htmx Factory Class

Drupal now provides a dedicated API to build HTMX attributes in PHP.

use Drupal\Core\Render\Htmx;

(new Htmx())
  ->get('/demo/replace')
  ->target('#content-wrapper')
  ->swap('outerHTML')
  ->applyTo($build['button']);

Benefits:

  •  - Clear, discoverable API
  • - No hardcoded attribute strings
  • - Better integration with render arrays

Example 1: Simple Partial Replacement

Controller

public function replace(): array {
  return [
    '#markup' => '<div id="content-wrapper">New content loaded</div>',
  ];
}

Render Array

$build['button'] = [
  '#type' => 'html_tag',
  '#tag' => 'button',
  '#value' => 'Load content',
];

(new Htmx())
  ->get('/demo/replace')
  ->target('#content-wrapper')
  ->swap('outerHTML')
  ->applyTo($build['button']);

No custom JavaScript is required.


Example 2: Dynamic Form Without AJAX API

One of the biggest improvements in Drupal 11.3 is native HTMX support in Form API.

Form Definition

public function buildForm(array $form, FormStateInterface $form_state): array {
  $makes = ['Audi', 'Toyota', 'BMW'];
  $models = [
    ['A1', 'A4', 'A6'],
    ['Land Cruiser', 'Tacoma', 'Yaris'],
    ['325i', 'X5', 'X7'],
  ];

  $form['make'] = [
    '#type' => 'select',
    '#title' => 'Make',
    '#options' => $makes,
  ];

  $form['model'] = [
    '#type' => 'select',
    '#title' => 'Model',
    '#options' => $models[$form_state->getValue('make', 0)] ?? [],
    '#wrapper_attributes' => ['id' => 'models-wrapper'],
  ];

  return $form;
}

HTMX Enhancement

(new Htmx())
  ->post()
  ->target('#models-wrapper')
  ->select('#models-wrapper')
  ->swap('outerHTML')
  ->applyTo($form['make']);

Whenever the user changes the car make, the models select is rebuilt on the server and replaced on the page.

No #ajax, no callbacks, no JavaScript.


Optimized Responses: onlyMainContent()

Drupal 11.3 introduces a specialized renderer that returns only the main content area for HTMX requests.

(new Htmx())
  ->post()
 ->onlyMainContent()
  ->target('#models-wrapper')
  ->swap('outerHTML')
  ->applyTo($form['make']);

This keeps responses small and fast, while remaining compatible with BigPipe.


HTMX-Specific Routes

Routes that exist solely to serve HTMX requests can enable a special option.

htmx.demo:
  path: '/htmx/demo'
  defaults:
    _controller: '\Drupal\demo\Controller\DemoController::replace'
    _title: 'HTMX Demo'
  requirements:
    _permission: 'access content'
  options:
    _htmx_route: TRUE

This disables full page rendering and returns only the relevant HTML fragment.


Example 3: Views Filtering and Pager with HTMX

One of the most common use cases for Drupal AJAX is Views filtering and pagination. Drupal 11.3 + HTMX allows you to implement this without the legacy AJAX API and with full BigPipe compatibility.

Scenario

  • A View listing products
  • Exposed filters (e.g. category)
  • Pager (next / previous)
  • Only the View should update, not the entire page

Step 1: Wrap the View Output

In a controller, block, or layout, wrap the View in a stable container.

$build['products'] = [
  '#type' => 'container',
  '#attributes' => ['id' => 'products-view-wrapper'],
  'view' => views_embed_view('products', 'page_1'),
];

This wrapper is critical. HTMX will always replace it as a whole.


Step 2: Enhance Exposed Filter with HTMX

Assume the exposed filter form is rendered above the View.

use Drupal\Core\Render\Htmx;

(new Htmx())
  ->get('/products')
  ->target('#products-view-wrapper')
  ->select('#products-view-wrapper')
  ->swap('outerHTML')
  ->applyTo($form['category']);

When the filter value changes:

  1. HTMX sends a GET request
  2. Drupal rebuilds the View with exposed input
  3. Only the View wrapper is replaced

No JavaScript and no Views AJAX settings required.


Step 3: Pager Links (Zero Configuration)

Pager links inside the View are plain <a> elements.

HTMX automatically intercepts clicks inside an HTMX context, so:

  • Clicking “Next” sends an HTMX request
  • Drupal renders the next page of the View
  • The wrapper is replaced

No additional configuration is required.


Step 4: Optimize the Route for HTMX

Define the View page route with the _htmx_route option.

view.products.page_1:
  options:
    _htmx_route: TRUE

This ensures:

  • No full page chrome
  • Smaller HTML responses
  • Faster swaps
  • Full BigPipe support

Why outerHTML Matters for Views

Views often contain:

  • Lazy builders
  • Blocks
  • Contextual filters

Replacing only innerHTML may leave stale BigPipe placeholders.

Best practice:

->swap('outerHTML')

Result

You get:

  • HTMX-powered filtering
  • HTMX-powered pagination
  • BigPipe streaming
  • Minimal JavaScript
  • Server-rendered HTML

All using standard Drupal Views.


HTMX and BigPipe: A Perfect Match

BigPipe streams HTML placeholders and replaces them asynchronously. HTMX works seamlessly with this approach because it:

  • Inserts real HTML (not JSON)
  • Executes <script> chunks correctly
  • Preserves BigPipe placeholders

Best practice:

->swap('outerHTML')

This ensures placeholder IDs remain consistent and prevents rendering issues.


When to Use HTMX in Drupal

HTMX is an excellent choice for:

  • Forms with dynamic elements
  • Views filtering and pagination
  • Admin UIs
  • Progressive enhancement
  • Reducing JavaScript payloads

It is not a replacement for fully decoupled architectures when you need:

  • Complex client-side state
  • Offline support
  • Native mobile applications

Conclusion

With Drupal 11.3, HTMX becomes a powerful new default for building interactive user interfaces:

  • Less JavaScript
  • Faster perceived performance
  • Cleaner server-side logic
  • Native integration with Form API and BigPipe

HTMX does not replace headless Drupal—but it significantly expands what can be done without leaving the Drupal rendering model.

For many projects, it represents the best balance between classic multi-page applications and fully decoupled frontends.

Tags

  • Drupal
  • HTMX
  • Web Performance
  • Server Rendering
  • Progressive Enhancement
  • PHP
  • BigPipe

Comments

About text formats

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Powered by Drupal