# Dennis Morello — Full Content > Dennis Morello, Lead Frontend Engineer from Milan. 10+ years building accessible web interfaces and open-source libraries like React Awesome Reveal. ## Projects - **Arbor** (https://github.com/morellodev/arbor): A friendly CLI for managing git worktrees. - **Clipwise AI** (https://www.getclipwise.app): Chrome extension that converts webpages to clean Markdown for ChatGPT, Claude, and other AI assistants. - **Defrag98** (https://defrag98.com): A simulator of the classic Windows 98 Disk Defragmenter tool. - **Investment Simulator** (https://money.morello.dev): A simple investment simulator to help you understand the power of compound interest. - **React Awesome Reveal** (https://github.com/awesome-reveal/react-awesome-reveal): Performant and easy-to-use animation library for React apps. ## Talks - **Writing Accessible Components** (https://youtu.be/wfjjFjoyRd0, 2022-11-09): Learn how to build React components that are accessible by default, with a live coding session building an accessible accordion from scratch. --- # htmx 4: What's New, What Breaks, Why It's Not latest URL: https://morello.dev/blog/htmx-4 Published: August 28, 2026 Tags: webdev, javascript, htmx, opensource htmx 4.0 is out: fetch() internals, explicit inheritance, no more history cache. What breaks, how to migrate, and why 2.x stays latest on npm until early 2027. htmx 4.0.0 [shipped today, August 28, 2026](https://four.htmx.org/announcements/2026-08-28-htmx-4.0.0-is-released). The ajax core now runs on `fetch()` instead of `XMLHttpRequest`, attribute inheritance is explicit, the back button issues a real request instead of restoring a cached snapshot, and error responses can be routed by status code. It is a major release that breaks real things, on purpose. And it comes with an unusual instruction: nobody has to move. On npm, 2.x keeps the `latest` dist-tag until early 2027, 4.0 lives under `next`, and htmx 2 stays supported indefinitely. ## TL;DR - Every request htmx makes now goes through `fetch()`; `XMLHttpRequest` is out, along with the XHR-specific events. - Attribute inheritance is explicit: attributes like `hx-confirm` reach descendants only when you add the `:inherited` modifier. - The history DOM cache is gone. The back button re-fetches the page from the server instead of restoring a stored snapshot. - Error responses swap by default, and the new `hx-status` attribute routes 4xx and 5xx responses by status code to their own targets. - On npm, htmx 2.x keeps the `latest` dist-tag until early 2027, and 4.0 lives under the `next` dist-tag, so nobody upgrades by accident. ## What changed in htmx 4 htmx 4 is a ground-up rework of the library's internals and a cleanup of its accumulated defaults. Carson Gross, htmx's creator, announced it in an essay called [The fetch()ening](https://htmx.org/essays/the-fetchening/) in November 2025, together with the first alpha. A string of betas followed through 2026, and the [final release landed today](https://github.com/bigskysoftware/htmx/releases). The big majors I covered this year, [Astro 7](/blog/astro-7) and [pnpm v12](/blog/pnpm-v12-rust-rewrite), rewrote their engines precisely so that nothing would change for you: same surface, faster internals. htmx 4 is the other kind of major. Moving to `fetch()` gave the project a reason to revisit design decisions that had been locked in for years, and the team took it. Implicit inheritance, the history cache, silently ignored error responses: all gone. That is why the release strategy matters as much as the feature list, and I will get to it. ## The fetch() rewrite Every request htmx makes now goes through `fetch()` rather than `XMLHttpRequest`. You do not call either API yourself when you write htmx, so on a quiet day you will not notice. The visible consequences are at the edges: - **XHR-specific events are gone.** `htmx:xhr:loadstart`, `htmx:xhr:progress`, and `htmx:xhr:abort` have no `fetch()` equivalent and were removed. - **Events follow a new `htmx:phase:action` naming scheme.** If you listen to htmx lifecycle events in JavaScript, every listener needs a rename. - **A default request timeout exists now.** Requests time out after 60 seconds; in 2.x they could hang forever. The config option moved from `timeout` to `defaultTimeout`. The event renames look like this: | htmx 2.x | htmx 4 | | --- | --- | | `htmx:beforeRequest` | `htmx:before:request` | | `htmx:afterRequest` | `htmx:after:request` | | `htmx:beforeSwap` | `htmx:before:swap` | | `htmx:afterSwap` | `htmx:after:swap` | | `htmx:configRequest` | `htmx:config:request` | | `htmx:sendError`, `htmx:swapError`, `htmx:targetError`, `htmx:timeout` | `htmx:error` | That last row is a real simplification: four error events collapse into one `htmx:error`. Extensions changed even more. The announcement puts it plainly: "Switching to `fetch()` internally let us rethink how extensions can and should work." The old `htmx.defineExtension()` API is replaced by `htmx.registerExtension()`, and the `hx-ext` attribute is gone entirely: loading an extension's script is enough. There is a dedicated [extension migration guide](https://four.htmx.org/docs/extension-htmx-4-migration-guide) for authors. The payoff is streaming: new first-party extensions stream HTML into the page over server-sent events (`hx-sse`), WebSockets (`hx-ws`), and `multipart/mixed` responses (`hx-multipart`). ## Attribute inheritance is now explicit In htmx 2, attributes like `hx-confirm` and `hx-target` silently inherited from parent elements. Convenient until it wasn't: a `hx-confirm` on a container would attach confirmation dialogs to descendants you forgot about, and `hx-disinherit` existed purely to fight this. In htmx 4, inheritance only happens when you ask for it with the `:inherited` modifier: ```html