Drupal 11.3 shipped with a fully integrated HTMX subsystem — and it's not a contrib experiment. It's the foundation for replacing Drupal's aging, jQuery-dependent Ajax API with a modern, declarative, hypermedia-driven approach. Here's what changed, why it matters, and how to start using it today.
The Background
Drupal's Ajax API has been a pain point for years. It relies on jQuery, returns proprietary JSON command arrays, and forces developers to learn Drupal-specific abstractions instead of web standards. Every interactive form element, every Views AJAX pager, every modal dialog runs through this system.
The idea to replace it with HTMX first appeared in 2023 as a core issue by effulgentsia. After a proof of concept by fathershawn in 2024, it became an official community initiative in 2025, led by Drupal's Frontend Framework Manager Theodore Biadala (nod_).
Drupal 11.2 added HTMX as a core dependency. Drupal 11.3 delivered the full toolset.
What's Actually New in 11.3
The Htmx Factory Class
The new \Drupal\Core\Htmx\Htmx class is the centerpiece. It provides a fluent API for building HTMX attributes on render arrays — so you never have to manually assemble data-hx-* attributes.
use Drupal\Core\Htmx\Htmx;
(new Htmx())
->post()
->target('#models-wrapper')
->select('#models-wrapper')
->swap('outerHTML')
->applyTo($form['make']);
This attaches data-hx-post, data-hx-target, data-hx-select, and data-hx-swap attributes to the make form element. When the user changes the select, HTMX fires a POST request and swaps the target element with the response fragment.
Extended FormBuilder
Drupal's FormBuilder now natively handles HTMX requests. When a form is rebuilt from an HTMX request, all submitted form values are available to the form class for dynamic restructuring — no custom controller needed.
HtmxRequestInfoTrait
A new trait for controllers, forms, and services:
use Drupal\Core\Htmx\HtmxRequestInfoTrait;
class MyController {
use HtmxRequestInfoTrait;
public function content(Request $request) {
if ($this->isHtmxRequest($request)) {
// Return only the HTML fragment
return new Response('<div id="result">Updated content</div>');
}
// Return the full page
return ['#markup' => '<div id="result">Initial content</div>'];
}
}
Route-Level HTMX Support
You can declare routes as HTMX-specific:
my_module.htmx_endpoint:
path: '/my-module/update'
defaults:
_controller: '\Drupal\my_module\Controller\MyController::content'
options:
_htmx_route: TRUE
The onlyMainContent() Method
For HTMX responses, you often want just the main content area without the full page chrome. The new onlyMainContent() method on responses strips everything except the main content and required assets — crucial for partial page updates.
Old vs. New: Side-by-Side
Here's a cascading select (car make → model) in both approaches.
The Old Way — Ajax API:
$form['make'] = [
'#type' => 'select',
'#title' => 'Make',
'#options' => $makes,
'#ajax' => [
'callback' => '::updateModels',
'wrapper' => 'models-wrapper',
'event' => 'change',
],
];
$form['model'] = [
'#type' => 'select',
'#title' => 'Model',
'#options' => $this->getModels($form_state->getValue('make')),
'#prefix' => '<div id="models-wrapper">',
'#suffix' => '</div>',
];
// Callback returns an AjaxResponse or render array
public function updateModels(array &$form, FormStateInterface $form_state) {
return $form['model'];
}
The New Way — HTMX:
$form['make'] = [
'#type' => 'select',
'#title' => 'Make',
'#options' => $makes,
];
$form['model'] = [
'#type' => 'select',
'#title' => 'Model',
'#options' => $this->getModels($form_state->getValue('make', 0)),
'#wrapper_attributes' => ['id' => 'models-wrapper'],
];
(new Htmx())
->post()
->target('#models-wrapper')
->select('#models-wrapper')
->swap('outerHTML')
->applyTo($form['make']);
No callback method. No wrapper div hacks. No AjaxResponse object. The form just rebuilds, and HTMX swaps the fragment. The FormBuilder handles the HTMX request natively.
The Performance Story
Drupal.org claims up to 71% reduction in loaded JavaScript for browser-server interactions, including HTML streaming with BigPipe. This makes sense — HTMX itself is ~14KB gzipped, while the legacy Ajax API pulls in jQuery plus a stack of Drupal-specific JS behaviors, commands, and event handlers.
For sites that already ship jQuery for other reasons, the savings are smaller. But for new builds where you can skip jQuery entirely, the difference is significant — especially on mobile.
Migration: What's Realistic
Let's be clear: the old Ajax API isn't going anywhere tomorrow. The migration path spans multiple Drupal versions, possibly years. Here's the current state:
Commands with documented HTMX replacements:
- ReplaceCommand → data-hx-swap="outerHTML"
- AppendCommand → data-hx-swap="beforeend"
- PrependCommand → data-hx-swap="afterbegin"
- AfterCommand → data-hx-swap="afterend"
- OpenDialogCommand / OpenModalDialogCommand → HTMX with dialog extensions
Commands that become unnecessary:
- AddCssCommand — HTMX handles asset loading via head-support extension
- AddJsCommand — same; scripts in swapped content execute automatically
Still in progress:
- BeforeCommand and several others are marked "needs work"
- InvokeCommand (arbitrary JS execution) has no direct HTMX equivalent by design — HTMX favors declarative HTML over imperative JS
The community tracks progress in #3535179 and collaborates daily in the #htmx channel on Drupal Slack.
What This Means for Module Developers
New modules: Use HTMX from the start. The Htmx factory class and HtmxRequestInfoTrait give you everything you need. Return HTML fragments from controllers, use declarative attributes on forms.
Existing modules: No rush to migrate, but consider it for new features. The Ajax API still works and will be supported through the transition. When you do migrate, the pattern is straightforward — replace #ajax callbacks with HTMX attributes, and simplify your controllers to return plain HTML instead of AjaxResponse objects.
Frontend developers: If you're maintaining custom Drupal behaviors that attach to Ajax responses, start thinking about how HTMX's event model (htmx:afterSwap, htmx:beforeRequest, etc.) maps to your existing code. The contributed HTMX module includes a debugger that logs all HTMX events to the browser console — useful during migration.
The Bigger Picture
This isn't just a Drupal story. HTMX gained more GitHub stars than React in 2024. The "HTML-over-the-wire" approach is winning converts across frameworks — Rails has Turbo, Laravel has Livewire, Django devs are adopting HTMX directly. Drupal bringing it into core isn't following a trend — it's making a structural commitment that reduces the framework's JavaScript surface and lowers the bar for new contributors who know HTML but not Drupal's proprietary Ajax protocol.
For Drupal developers who already use HTMX in their non-Drupal projects (raises hand), this is the best core change in years.
Resources:
- Official Drupal blog post: Native HTMX in Drupal 11.3.0
- Community initiative: Replace Ajax API with HTMX
- Ajax API to HTMX migration guide
- Htmx class API reference
- DrupalCon Chicago 2026 session
- HTMX with forms in Drupal 11.3 tutorial