← All posts

htmx 4.0: fetch-based requests and HTML-driven apps

Major htmx 4.0: fetch instead of XHR, attributes inherit only with :inherited, built-in morph swaps, and <hx-partial> for multi-target updates.

htmx 4.0: fetch-based requests and HTML-driven apps
Contents

In brief

htmx 4.0 is a major step for apps that drive interactivity from markup instead of a thick client script. Internally, requests use fetch() instead of XMLHttpRequest; attributes no longer inherit to descendants unless you opt in with :inherited; morphing swaps (innerMorph / outerMorph) ship built-in; and <hx-partial> lets one response update several targets. Docs still open with a working example in a few lines of HTML—easy to judge if the approach fits.

What happened

Familiar hx-get / hx-post and swap targets still feel like htmx, but three decisions change upgrades. The biggest is inheritance: in 2.x a parent attribute often applied to children “like CSS”; in 4.x that only happens with :inherited. Classic footgun—hx-confirm or CSRF headers on a wrapper—silently stop reaching a child hx-delete without the suffix. The team ships npx [email protected] upgrade-check over templates plus a 2.x compatibility extension for gradual migration.

Moving to fetch() simplifies extensions (streaming, downloads) and drops old htmx:xhr:* events. Event names follow htmx:phase:action (for example htmx:before:request). History no longer snapshots the DOM into localStorage by default: back navigation re-fetches and swaps—fewer surprises from third-party script mutations.

For markup authors, morph swaps merge HTML into the live DOM (focus, field values, media state), and <hx-partial hx-target="…"> makes multi-target updates explicit in the response—clearer than out-of-band swaps alone.

Also noted briefly in Frontend Focus #756: Firefox 155 with a larger CSS haul (attr() more widely, progress(), alpha()) and the final removal of Manifest V2 extensions from the Chrome Web Store (including uBlock Origin). Surrounding news for the week—not the htmx story.

Why it matters

htmx sells server-driven UI from HTML. Implicit inheritance was powerful and confusing: editing a parent could change deep button behavior without an obvious local cue. Explicit :inherited makes templates readable at the cost of migration work. Morph and <hx-partial> cover common “rich screen” pains without standing up a full SPA layer: keep local state across updates, and touch a list and a counter in one response.

Product detail matters too: npm latest stays on 2.x until early 2027; the 4.x line is next, so unversioned CDNs do not force-upgrade. You can plan the move instead of discovering it by accident.

In practice

  1. Before switching versions, run upgrade-check on HTML/templates: it flags missing :inherited, old event names, and removed attributes.
  2. Audit parent hx-headers, hx-confirm, and hx-target above elements that issue hx-* requests—those almost always need :inherited.
  3. Where focus and inputs matter, try hx-swap="innerMorph" or outerMorph instead of a full node replace; stable ids help the morph algorithm.
  4. Prefer <hx-partial> with an explicit hx-target when one server response should update several regions—easier to maintain than a pile of out-of-band blocks.
  5. For a soft landing, use the 2.x compatibility extension; in production, pin the script version instead of a bare CDN latest.

Takeaway

htmx 4.0 doubles down on “behavior in markup”: modern requests via fetch(), honest attribute inheritance, morph swaps out of the box, and explicit multi-updates with <hx-partial>. Migrate on purpose—with the checker and template tests—rather than waiting for latest to flip someday.