AJAX Integration
When to Use
Understanding the landscape of dynamic content loading in Drupal - choosing between legacy AJAX API and modern HTMX approach.
Decision
Critical context shift in Drupal 11.x: the htmx library was vendored into Drupal core in 11.2, and the developer-facing Drupal\Core\Htmx\Htmx API plus the Ajax subsystem integration landed in 11.3.0. HTMX is a modern, declarative alternative to the traditional AJAX API. This section provides orientation - detailed coverage lives in dedicated guides.
Choose your path: - HTMX (Drupal 11.3+) - Modern, declarative, HTML-first. Start here for new development. - AJAX API (legacy) - Imperative, callback-based. Required for Drupal 10.x and existing systems, where HTMX is unavailable before 11.3.
Key architectural difference: - AJAX = Imperative (JavaScript callbacks, command objects) - HTMX = Declarative (HTML attributes, server responses interpreted by the htmx client library)
Pattern
HTMX approach (Drupal 11.3+, declarative):
use Drupal\Core\Htmx\Htmx;
// Make a select element interactive
$htmx = new Htmx();
$htmx->post()
->select('*:has(>select[name="config_name"])')
->target('*:has(>select[name="config_name"])')
->swap('outerHTML');
$htmx->applyTo($form['config_type']);
// Result: HTML attributes, no JavaScript callbacks needed
// <select data-hx-post="/form-url" data-hx-select="..." data-hx-target="..." data-hx-swap="outerHTML">
AJAX API approach (legacy, imperative):
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
// Callback function
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response->addCommand(new InvokeCommand('.selector', 'addClass', ['active']));
return $response;
}
// Form element
$form['element'] = [
'#ajax' => [
'callback' => '::ajaxCallback',
'event' => 'change',
'wrapper' => 'result-wrapper',
],
];
JavaScript integration with HTMX (automatic via core):
// From core/misc/htmx/htmx-behaviors.js
// Drupal.behaviors automatically run on HTMX-loaded content
htmx.on('htmx:drupal:load', ({ detail }) => {
Drupal.attachBehaviors(detail.elt, drupalSettings);
});
htmx.on('htmx:drupal:unload', ({ detail }) => {
Drupal.detachBehaviors(detail.elt, drupalSettings, 'unload');
});
Behavior AJAX/HTMX compatibility (works with both):
Drupal.behaviors.dynamicContent = {
attach(context, settings) {
// Works on initial load AND after AJAX/HTMX updates
// No special handling needed if using context
once('dynamic', '.content', context).forEach(function (element) {
element.addEventListener('click', handleClick);
});
}
};
Common Mistakes
- Not using context in behaviors - WHY: AJAX/HTMX-loaded content doesn't initialize, mysterious bugs
- Manually re-initializing after updates - WHY: Drupal does this automatically via
Drupal.attachBehaviors()/Drupal.detachBehaviors()on both systems - Missing detach() for destroyed content - WHY: Event listeners remain, memory leaks accumulate
- Choosing AJAX for new Drupal 11.3+ projects - WHY: HTMX is simpler, more maintainable, and officially supported for new development
See Also
Within this guide: - Drupal.behaviors Pattern - AJAX/HTMX-compatible initialization - Once API - Preventing duplicate processing
Dedicated guides for deep coverage: - Drupal AJAX Framework - Comprehensive AJAX API reference (callbacks, commands, forms) - Drupal HTMX - HTMX implementation patterns (the modern approach) - AJAX to HTMX Migration - Converting AJAX to HTMX pattern-by-pattern
Core references:
- Reference: /core/lib/Drupal/Core/Htmx/Htmx.php - HTMX utility class
- Reference: /core/misc/htmx/htmx-behaviors.js - Drupal.behaviors integration
- Reference: /core/misc/ajax.js - Legacy AJAX implementation
- Reference: Official AJAX API Documentation
- Reference: "Ajax subsystem now includes HTMX" change record (Drupal 11.3.0)