Use when developing HTMX features in Drupal 11.3+ or migrating AJAX to HTMX. Covers Htmx class usage, form patterns, migration strategies, and validation. Triggers on "htmx", "ajax to htmx", "dynamic form", "dependent dropdown".
Resources
1Install
npx skillscat add camoa/claude-skills/htmx-development Install via the SkillsCat registry.
HTMX Development
Drupal 11.3+ HTMX implementation and AJAX migration guidance.
When to Use
- Implementing dynamic content updates in Drupal
- Building forms with dependent fields
- Migrating existing AJAX to HTMX
- Adding infinite scroll, load more, real-time validation
- NOT for: Traditional AJAX maintenance (use ajax-reference.md)
Decision: HTMX vs AJAX
| Choose HTMX | Choose AJAX |
|---|---|
| New features | Existing AJAX code |
| Declarative HTML preferred | Complex command sequences |
| Returns HTML fragments | Dialog commands needed |
| Progressive enhancement needed | Contrib expects AJAX |
Hybrid OK: Both systems coexist. Migrate incrementally.
Quick Start
1. Basic HTMX Element
use Drupal\Core\Htmx\Htmx;
use Drupal\Core\Url;
$build['button'] = [
'#type' => 'button',
'#value' => t('Load'),
];
(new Htmx())
->get(Url::fromRoute('my.content'))
->onlyMainContent()
->target('#result')
->swap('innerHTML')
->applyTo($build['button']);2. Controller Returns Render Array
public function content() {
return ['#markup' => '<p>Content loaded</p>'];
}3. Route (Optional HTMX-Only)
my.content:
path: '/my/content'
options:
_htmx_route: TRUE # Always minimal responseCore Patterns
Pattern Selection
| Use Case | Pattern | Key Methods |
|---|---|---|
| Dependent dropdown | Form partial update | select(), target(), swap('outerHTML') |
| Load more | Append content | swap('beforeend'), trigger('click') |
| Infinite scroll | Auto-load | swap('beforeend'), trigger('revealed') |
| Real-time validation | Blur check | trigger('focusout'), field update |
| Multi-step wizard | URL-based steps | pushUrl(), route parameters |
| Multiple updates | OOB swap | swapOob('outerHTML:#selector') |
Dependent Dropdown
public function buildForm(array $form, FormStateInterface $form_state) {
$form['category'] = ['#type' => 'select', '#options' => $this->getCategories()];
(new Htmx())
->post(Url::fromRoute('<current>'))
->onlyMainContent()
->select('#edit-subcategory--wrapper')
->target('#edit-subcategory--wrapper')
->swap('outerHTML')
->applyTo($form['category']);
$form['subcategory'] = ['#type' => 'select', '#options' => []];
// Handle trigger
if ($this->getHtmxTriggerName() === 'category') {
$form['subcategory']['#options'] = $this->getSubcategories(
$form_state->getValue('category')
);
}
return $form;
}Reference: core/modules/config/src/Form/ConfigSingleExportForm.php
Multiple Element Updates
// Primary element updates via target
// Secondary element updates via OOB
(new Htmx())
->swapOob('outerHTML:[data-secondary]')
->applyTo($form['secondary'], '#wrapper_attributes');URL History
(new Htmx())
->pushUrlHeader(Url::fromRoute('my.route', $params))
->applyTo($form);Htmx Class Reference
Request Methods
get(Url)/post(Url)/put(Url)/patch(Url)/delete(Url)
Control Methods
target(selector)- Where to swapselect(selector)- What to extract from responseswap(strategy)- How to swap (outerHTML, innerHTML, beforeend, etc.)swapOob(selector)- Out-of-band updatestrigger(event)- When to triggervals(array)- Additional valuesonlyMainContent()- Minimal response
Response Headers
pushUrlHeader(Url)- Update browser URLredirectHeader(Url)- Full redirecttriggerHeader(event)- Fire client eventreswapHeader(strategy)- Change swapretargetHeader(selector)- Change target
See: references/quick-reference.md for complete tables
Detecting HTMX Requests
In forms (trait included automatically):
if ($this->isHtmxRequest()) {
$trigger = $this->getHtmxTriggerName();
}In controllers (add trait):
use Drupal\Core\Htmx\HtmxRequestInfoTrait;
class MyController extends ControllerBase {
use HtmxRequestInfoTrait;
protected function getRequest() { return \Drupal::request(); }
}Migration from AJAX
Quick Conversion
| AJAX | HTMX |
|---|---|
'#ajax' => ['callback' => '::cb'] |
(new Htmx())->post()->applyTo() |
'wrapper' => 'id' |
->target('#id') |
return $form['element'] |
Logic in buildForm() |
new AjaxResponse() |
Return render array |
ReplaceCommand |
->swap('outerHTML') |
HtmlCommand |
->swap('innerHTML') |
AppendCommand |
->swap('beforeend') |
MessageCommand |
Auto-included |
Migration Steps
- Identify
#ajaxproperties - Replace with
Htmxclass - Move callback logic to
buildForm() - Use
getHtmxTriggerName()for conditional logic - Replace
AjaxResponsewith render arrays - Test progressive enhancement
See: references/migration-patterns.md for detailed examples
Validation Checklist
When reviewing HTMX implementations:
-
Htmxclass used (not raw attributes) -
onlyMainContent()for minimal response - Proper swap strategy selected
- OOB used for multiple updates
- Trigger element detection works
- Works without JavaScript (progressive)
- Accessibility:
aria-livefor dynamic regions - URL updates for bookmarkable states
Common Issues
| Problem | Solution |
|---|---|
| Content not swapping | Check target() selector exists |
| Wrong content extracted | Check select() selector |
| JS not running | Verify htmx:drupal:load fires |
| Form not submitting | Check post() and URL |
| Multiple swaps fail | Add swapOob('true') to elements |
| History broken | Use pushUrlHeader() |
References
Bundled (HTMX-Specific)
references/quick-reference.md- Command equivalents, method tablesreferences/htmx-implementation.md- Full Htmx class API, detection, JSreferences/migration-patterns.md- 7 patterns with before/after codereferences/ajax-reference.md- AJAX commands for understanding existing code
Online Dev-Guides (Drupal Domain)
For Drupal domain context when analyzing, recommending, or validating HTMX patterns, fetch the guide index:
Index: https://camoa.github.io/dev-guides/llms.txt
Likely relevant topics: forms, routing, js-development, render-api
Usage: WebFetch the index to discover available topics, then fetch specific topic pages for Drupal patterns when the bundled HTMX references don't cover the underlying Drupal concept.
Key Files in Drupal Core
core/lib/Drupal/Core/Htmx/Htmx.php- Main APIcore/lib/Drupal/Core/Htmx/HtmxRequestInfoTrait.php- Request detectioncore/lib/Drupal/Core/Render/MainContent/HtmxRenderer.php- Response renderercore/modules/config/src/Form/ConfigSingleExportForm.php- Production examplecore/modules/system/tests/modules/test_htmx/- Test examples