htmx-development
1
总安装量
1
周安装量
#49174
全站排名
安装命令
npx skills add https://github.com/camoa/claude-skills --skill htmx-development
Agent 安装分布
mcpjam
1
claude-code
1
windsurf
1
zencoder
1
crush
1
cline
1
Skill 文档
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 response
Core 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
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
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