Skip to content

Building HTMX-powered interfaces with ECA

HTMX lets you build dynamic, interactive interfaces by adding attributes to HTML elements, without writing JavaScript. Drupal core ships HTMX and a small integration layer, and the eca_htmx module exposes that integration to ECA models. This lets you build auto-refreshing regions, click-to-load fragments, and dynamic responses entirely from the modeler.

This guide explains what Drupal core provides, which ECA plugins map to those features, and how to combine them into working patterns.

How HTMX works in Drupal

HTMX is a declarative markup system. You add data-hx-* attributes to an element, and the HTMX JavaScript library turns them into AJAX requests that swap the response into the page. For example, an element with data-hx-get="/clock" and data-hx-trigger="every 1s" polls /clock once a second and replaces its own content with the response.

Drupal core provides three building blocks:

  • The core/drupal.htmx library — the HTMX runtime plus Drupal glue that re-attaches Drupal behaviors to swapped-in content and loads any new CSS and JavaScript the response requires.
  • The minimal response renderer — when a request is marked as an HTMX request, core returns only the page's main content wrapped in a bare HTML document instead of the full themed page (header, blocks, and footer are omitted). The minimal response also carries an X-Robots-Tag: noindex header so the fragment is not indexed by search engines.
  • HTMX request and response headers — HTMX sends HX-* request headers (such as HX-Request and HX-Trigger) and honors HX-* response headers (such as HX-Redirect and HX-Trigger) to coordinate client behavior.

The eca_htmx module builds entirely on these core building blocks. It never ships its own HTMX JavaScript, and it produces all attributes and headers through core's HTMX helper, so the markup stays consistent with the rest of Drupal.

The two ways to request a minimal response

A recurring source of confusion is that core offers two independent ways to get the minimal (main-content-only) response, and they take effect at different stages of the request lifecycle. They are not interchangeable.

_htmx_route route option
Set on a route while routes are being built. Any request to a route carrying this option returns the minimal response. Use the HTMX: mark route action, which reacts to the Alter route event.
_wrapper_format=drupal_htmx query parameter
Added to an individual request at runtime. Only that request returns the minimal response. Use the HTMX: minimal response action, or let the polling and element actions add it automatically through their only main content option.

Which one to use

Mark the route when you control the route and want every request to it to return a fragment (for example, a dedicated endpoint that only ever serves a fragment). Mark the request when you want a specific interaction to fetch a fragment from a route that otherwise renders a full page.

The plugins

Building HTMX elements

HTMX: render element
Builds a single element carrying the HTMX attributes you configure: the request method and URL, the trigger, the swap strategy, the target and select selectors, and options such as a confirmation dialog, indicators, pushed URLs, and request values. Use this for click-to-load fragments, infinite scroll, and similar interactions.
HTMX: poll region
A specialized element that polls a URL on an interval and swaps in the response. Use it for content that refreshes on its own, such as a clock, a status indicator, or a notification count. The only main content option is enabled by default, so the polled URL automatically returns a fragment.

Both actions render an element. Place them in a block, an entity display, or any other render context, and they attach core/drupal.htmx automatically.

Reacting to HTMX requests

HTMX: is request
A condition that is true when the current request is an HTMX request. Use it to branch a model so that it builds a fragment for HTMX requests and a full page otherwise.
HTMX: compare request value
A condition that compares a value from the current HTMX request — such as the target element, the trigger, or the current URL — against an expected value.

The same request information is available as tokens, so you can read the HTMX target or trigger anywhere a token is accepted.

Shaping HTMX responses

HTMX: set response header
Sets any of the HTMX HX-* response headers on the current response, for example HX-Redirect to redirect the client, HX-Trigger to fire a client-side event, or HX-Retarget to change where the response is swapped. The HX-Location header supports its full form (source, event, handler, target, swap, values, headers, and select).

Because this action shapes the response, it reacts to a response event such as the Response created event or an ECA endpoint response.

Serving fragments with ECA endpoints

The natural source for an HTMX fragment is an ECA endpoint. An endpoint gives you a URL that an ECA model fully controls: the model builds the response content and can attach HTMX response headers. Because you control the endpoint route, its response is already a fragment — no full page chrome to strip — and it pairs naturally with a polling region or a click-to-load element that points at it.

Common patterns

Auto-refreshing region

  1. Event: any event that builds a render element, such as a block build.
  2. Action: HTMX: poll region — set the URL to an endpoint that returns the fragment, and the interval in seconds.

The region polls the endpoint and swaps in the response on every interval. Leave only main content enabled so the endpoint returns a fragment.

Click-to-load content

  1. Event: any event that builds a render element.
  2. Action: HTMX: render element — set the method to GET, the URL to the fragment endpoint, the trigger to click, and the target to the element that should receive the content.

Serve a fragment from an endpoint

  1. Event: ECA endpoint response — define the endpoint path.
  2. Action: Set response content — build the fragment markup.
  3. Action (optional): HTMX: set response header — fire a client-side event with HX-Trigger, for example to notify other parts of the page that the fragment refreshed.

Turn an existing route into a fragment

  1. Event: Alter route.
  2. Action: HTMX: mark route — set the route name (a * wildcard is supported) to mark as an HTMX route.

Every request to a marked route then returns the minimal response.