Drupal
Drupal HTMX on production catalogues in 2026
Drupal HTMX landed in core as the long-term replacement for much of the old AJAX Form API, and I treat it as a production tool on Drupal 11.3 and newer catalogues rather than a demo gimmick. I am Alan Vo, a Gold Coast web developer with 18 years on Drupal property listings, regulators, and museum collections. When a filter form, dependent select, or inline admin widget used to mean #ajax callbacks and a growing drupal.ajax footprint, HTMX gives me declarative data-hx-* attributes and a \Drupal\Core\Htmx\Htmx factory that keeps render arrays readable. This note covers how I roll Drupal HTMX out without breaking reverse proxies, BigPipe, or the JSON:API clients I still ship beside progressive enhancement.
The audience is site builders and module authors on Drupal 11.3.x or 11.4.x who maintain editorial or catalogue UIs in PHP. It complements Drupal JSON:API on live catalogues when the public app stays decoupled but admin or embedded widgets need server-rendered HTML fragments. It also pairs with Drupal Navigation module on production sites because both changes land in the same upgrade window. Facts below come from the Drupal 11.3 HTMX announcement, the HTMX change record, and web server header requirements.
Why Drupal HTMX matters on catalogue sites in 2026
Drupal HTMX matters on catalogue sites in 2026 because core now ships the library as a first-class dependency and documents up to 71% less JavaScript for browser-server interactions compared with the legacy AJAX stack, including streaming cases that interact with BigPipe. That number is from Drupal.org release messaging, not from my client analytics, but the direction matches what I see when a property filter or exhibition date picker stops pulling the full drupal.ajax bundle for a single wrapper swap.
Catalogue work still needs dependent fields: choose a region, reload available venues; pick a collection, reload faceted options. HTMX expresses that as POST or GET from the triggering element, a CSS target, and a swap strategy, which is easier to review in a merge request than opaque #ajax definitions scattered across form classes. Drupal prefixes attributes with data-hx-* so markup stays valid HTML, as described in the core blog post. Decoupled front ends are unchanged. HTMX is for in-theme or admin HTML that should stay on the server render pipeline.
Drupal 11.3 also extended FormBuilder so values survive HTMX rebuilds, which is the difference between a toy select and a production dependent form. Without that behavior, partial rebuilds drop user input and editors blame the CMS. I upgrade to at least 11.3.0 before I author new HTMX forms, and I patch to current 11.4.x on long-lived catalogues for unrelated entity-loading wins that make the same HTMX endpoints cheaper under load.
How Drupal HTMX works in core render arrays
Drupal HTMX works in core through the \Drupal\Core\Htmx\Htmx class, which maps HTMX attributes and response headers onto render arrays the same way #attributes always have. The API documentation shows the intended pattern: instantiate Htmx, chain verbs such as post(), targeting methods such as target() and select(), set swap() to values like outerHTML, then applyTo($element) on the form child that should trigger the request.
A minimal mental model mirrors upstream HTMX: any element can issue GET, POST, PUT, PATCH, or DELETE; any DOM event can trigger the request via data-hx-trigger; any selector can receive the response via data-hx-target; swap mode controls whether inner HTML or the whole node is replaced. Drupal's factory emits the data-hx-* names consistently so Twig output stays standards-friendly.
For responses, core adds a wrapper renderer and format aimed at HTMX requests so the browser receives only the fragment plus attached assets, not an entire page shell. You can opt in through HTMX attributes on the triggering element or through route options when you define a dedicated HTMX route (the change record notes this helps Views and other dynamic routes over time). Smaller payloads matter on museum and property catalogues where a filter bar sits above a heavy View.
Static helpers such as Htmx::createFromRenderArray() let you read HTMX settings back out of a render array when altering someone else's form, which is common in contrib patches. The class is marked internal by policy, meaning signatures can shift with HTMX library updates, so I pin core minors in composer and read release notes before bulk refactors.
The contributed htmx project (2.x on Drupal 11.3 through 11.5) adds tooling around core integration. I install it when I want module-level helpers beyond what I am willing to maintain in custom code, not because core HTMX is incomplete for basic forms.
Drupal HTMX production checklist before go-live
A Drupal HTMX production checklist starts with infrastructure, because HTMX fails silently when HX-* headers never reach PHP. The web server requirements page lists required request headers (HX-Request, HX-Target, HX-Trigger, and others) and response headers (HX-Location, HX-Push-Url, HX-Redirect, and others). CDN or WAF rules that strip unknown headers are the first thing I fix.
- Core version. Require Drupal 11.3.0 or newer for full FormBuilder integration; stay on supported 11.4.x patches for security and performance fixes unrelated to HTMX but shipped in the same deploy train.
- Header allow lists. On nginx, Apache, and upstream proxies, confirm
HX-*passes in both directions. Staging tests with browser devtools network panels are not enough; I curl with-H 'HX-Request: true'through the same hostname editors use. - Staging repro of each widget. Document trigger element, route, target selector, and swap mode. Capture HTML before and after swap to verify accessible labels and focus order.
- Asset attachment. Fragment responses must still attach libraries the swapped markup needs. If CSS disappears after swap, the wrapper format or
#attachedarrays are wrong, not HTMX itself. - BigPipe and streaming. Read release notes when a page uses BigPipe placeholders. HTMX coexists with streaming in core messaging, but I still test the heaviest catalogue page with HTMX filters enabled because contrib placeholders vary.
- Caching contexts. A fragment that embeds user-specific grants must carry correct cache metadata on the render array. Anonymous catalogue filters can cache; per-user admin widgets cannot reuse the same max-age.
- CSRF and methods. POST rebuilds on forms must keep form tokens valid. I avoid caching POST responses at the CDN and I verify method override behavior if PUT or PATCH appear in custom routes.
- Fallback without JavaScript. Catalogue pages should degrade to full submit or full page reload where feasible. HTMX is progressive enhancement, not an excuse to ship a dead page when JavaScript fails.
- Monitoring. Log 4xx and 5xx on HTMX routes separately in staging. Production alerts on spikes after deploy.
- Editor training. When HTMX changes admin widgets, update internal screenshots. Editors do not care that the transport is HTMX; they care that the venue list still loads.
What breaks when you ship Drupal HTMX carelessly
What breaks when you ship Drupal HTMX carelessly is almost always configuration and selectors, not the HTMX library. Stripped HX-Request headers produce full HTML pages swapped into a sidebar div, which looks like a layout explosion but is a proxy misconfiguration. Wrong data-hx-target selectors update nothing, and editors assume the catalogue is empty. Duplicate IDs in swapped fragments break targets because HTMX resolves the first match.
Form state bugs appear when core is below 11.3 or when custom code rebuilds forms without respecting the HTMX FormBuilder changes: values vanish, validation errors attach to the wrong wrapper, or file fields behave inconsistently. I reproduce on staging with the same core patch line as production.
Accessibility requires manual passes. Swapping outerHTML on a focused control drops focus unless I plan focus management or use patterns that preserve the active element. Dynamic filters on public catalogues need live region or label announcements when results change, especially for screen reader users browsing collection search on culture sites.
Security reviews sometimes flag HTMX because arbitrary elements can POST. I keep permissions on routes strict, never expose privileged HTMX endpoints to anonymous users unless the response is identical to a public View, and I rate-limit aggressive filter endpoints on property sites that bots hammer.
Mixing HTMX with aggressive page cache modules without #cache contexts causes editors to see another user's filter state. Treat swapped fragments like any other personalized render array.
How to measure Drupal HTMX on live catalogues
How to measure Drupal HTMX on live catalogues combines payload size, backend time, and editor task success. I compare staging before and after: total JavaScript kilobytes on a catalogue filter page, number of requests on first interaction, and TTFB on the HTMX route alone using browser performance tools. Drupal.org cites large JavaScript reductions for HTMX-driven flows; I verify on the actual theme because contrib modules may still attach legacy AJAX libraries elsewhere on the page.
Server-side, I watch database query counts on the HTMX rebuild route using existing APM or web profiler in staging. HTMX does not fix slow Views by itself. If a dependent select triggers a heavy query, optimize the View or Search API index before celebrating smaller JS.
Editor metrics stay qualitative unless you run structured usability tests: time to refine a property search, error rate choosing incompatible filters, support tickets mentioning "spinning" or "blank list". For museum catalogues I log when HTMX endpoints return non-200 responses and correlate with deploy times.
Do not use front-end conversion percentages I have not published on alanvo.com work cases. Platform statistics from Drupal.org are fine with a link; client metrics stay private unless they already appear on a case study page.
Related Drupal catalogue work on this site
Related Drupal catalogue work on this site includes Royal Ontario Museum, where collection discovery and editorial depth reward careful partial updates instead of full page reloads on every filter change, and NDIS Quality and Safeguards Commission, where regulator content must stay accessible when interactive widgets change. Immobel is the property catalogue pattern: dependent geography fields and search widgets are exactly where I reach for server-driven HTMX before adding a separate JavaScript bundle. When the public experience is decoupled, I still document which routes are HTMX-only so mobile apps do not accidentally depend on fragment HTML.
FAQ
When should I choose Drupal HTMX instead of JSON:API for a catalogue feature?
Choose Drupal HTMX when the UX should stay in server-rendered Twig, permissions follow normal Drupal routes, and you want HTML fragments without maintaining a parallel JavaScript client. Choose JSON:API when multiple consumers need structured JSON, offline mobile clients matter, or front-end state is owned by a separate framework. Many sites I ship use JSON:API for the public catalogue and HTMX for admin filters or embedded widgets in the theme.
Which Drupal core version do I need for production HTMX forms?
You need Drupal 11.3.0 or newer for the FormBuilder behavior that preserves input across HTMX rebuilds, as documented in the 11.3 release materials. HTMX library dependency arrived earlier, but I do not treat pre-11.3 cores as complete for dependent forms. Patch within 11.4.x for security and performance fixes that reduce cost on the same endpoints.
Do I have to change nginx or Cloudflare for Drupal HTMX?
You must allow HTMX request and response headers end to end if intermediaries filter unknown headers. Drupal documents HX-Request, HX-Target, HX-Trigger, and related names in web server requirements. Without them, Drupal may return full pages into fragment targets or HTMX may not detect AJAX requests correctly.
Is the contributed htmx module required on Drupal 11.3?
Core integration is sufficient for many custom forms using \Drupal\Core\Htmx\Htmx. The contributed htmx module 2.x adds extra tooling for teams that want module-level helpers; I install it when it saves repeated custom code, not as a hard requirement for every site.
Keep reading
Contact if you want this kind of work on a live store.