· htmx / htmx-2 / review

HTMX 2 review: what changed, what broke, and is it ready?

HTMX 2.0.10 is production-ready for backend-owned CRUD apps. Three breaking changes from v1, a migration escape hatch, real performance numbers, and a decision matrix against Alpine.js, Stimulus, and React.

By

1,632 words · 9 min read

Use HTMX 2 if your team is backend-first and you are building CRUD features, admin dashboards, or internal tools. If you need complex client-side state, optimistic updates, or offline support, HTMX will run out of road — pair it with Alpine.js or use React.

Who this is for

Backend developers (Python, Go, Ruby, Node) deciding whether HTMX 2 belongs in their next project. This assumes you already know what HTMX is. If you want the architectural case for HTMX vs React, see HTMX vs React 2026. This article is specifically about the v2 release: what actually changed, what breaks, and whether the project is stable enough to bet on.

What we reviewed

[email protected], released 2026-04-21.

Sources: the official changelog, v1 → v2 migration guide, Hacker News HTMX 2.0.0 thread, two published migration case studies, and State of JS 2024.

The three breaking changes that matter

HTMX 1.x to 2.0 is not a drop-in upgrade. These three changes will break working code.

DELETE requests now use URL parameters

HTMX 1.x sent DELETE payloads in the form body. Version 2 moved them to URL parameters to comply with the HTTP spec. If your API reads DELETE bodies, add this before loading htmx:

htmx.config.methodsThatUseUrlParams = ["get"]  // restores v1 behavior
// v2 default: ["get", "delete"]

Extensions left the building

Every extension — SSE, WebSockets, preload, idiomorph, head-support — was removed from the core distribution and now lives at extensions.htmx.org.

The SSE extension is a hard break. The v1 SSE extension will not work with htmx 2. You must upgrade the extension separately.

From the official release post: “All extensions have been moved out of the core repository to their own repo and website.”

hx-on single-attribute syntax is gone

Old (v1):

<button hx-on="htmx:beforeRequest: alert('sending...')">Click</button>

New (v2):

<button hx-on:htmx:before-request="alert('sending...')">Click</button>

The single-attribute form is removed in favor of the hx-on:event-name syntax. Note the kebab-case in the attribute name (htmx:before-request, not htmx:beforeRequest). This is a find-and-replace migration.

Config defaults that changed quietly

Three defaults shifted in v2:

Configv1v2
scrollBehavior'smooth''instant'
methodsThatUseUrlParams["get"]["get", "delete"]
selfRequestsOnlyfalsetrue

The selfRequestsOnly: true change is the most security-relevant: cross-domain AJAX is blocked by default in v2. If you were relying on cross-origin requests, you will need to explicitly disable this.

The migration escape hatch

For teams that cannot migrate all at once, the htmx-1-compat extension restores v1 behavior in one line:

<body hx-ext="htmx-1-compat">

This covers: old hx-on syntax, smooth scroll, DELETE form body, cross-domain requests, and the legacy hx-ws/hx-sse attributes. It cannot restore IE11 support — that is permanently dropped.

What v2 adds

hx-disabled-elt: Disable specific elements during a request, not just the triggering element.

<form hx-post="/save" hx-disabled-elt="#submit-btn, #cancel-btn">

hx-on: wildcard inline events: Handle htmx lifecycle events inline, without a <script> block.

<button hx-on:htmx:before-request="this.disabled = true"
        hx-on:htmx:after-request="this.disabled = false">
  Submit
</button>

View Transitions API: Use transition:true in hx-swap for animated DOM updates, or enable globally with htmx.config.globalViewTransitions = true.

responseHandling config: A configurable array of HTTP status code handlers. You can now control which codes trigger swaps and which trigger error handling — replacing the old hardcoded 200-only behavior.

Better security defaults: inlineScriptNonce and inlineStyleNonce config options add CSP nonce support.

DX in practice

Here is what inline form validation looks like in HTMX 2 with Django — using two of the new v2-only features:

# views.py
def validate_email(request):
    email = request.GET.get('email', '')
    taken = User.objects.filter(email=email).exists()
    return render(request, 'partials/email_feedback.html', {'taken': taken})
<input type="email"
       name="email"
       hx-get="/validate-email"
       hx-trigger="blur"
       hx-target="#email-feedback"
       hx-disabled-elt="this"
       hx-on:htmx:before-request="this.classList.add('validating')"
       hx-on:htmx:after-request="this.classList.remove('validating')">
<div id="email-feedback"></div>

The hx-disabled-elt="this" disables the input during validation. The hx-on: handlers add and remove a CSS class without a <script> tag. The response is a plain HTML fragment. No JSON, no client-side rendering, no state management.

Reactions from the HN v2.0.0 thread capture the shift in experience well: “With the addition of maybe 5 htmx attributes I was able to delete about 500 lines of client side JS. One of the happiest days of coding in the last 10 years.” Another commenter with 20 years across jQuery, Backbone, Ember, React, and Vue: “I genuinely think htmx is the only one I have liked even after I built something substantial in it.”

Performance

Bundle size

~14 KB min+gzipped (GitHub README). No build step required.

LibraryVersionMin+Gz
HTMX2.0.10~14 KB
Alpine.js3.15.12~13.5 KB
Stimulus3.2.2~10.9 KB
React + ReactDOM19.x~44–47 KB

HTMX + Alpine.js together: 27.5 KB — still smaller than React+ReactDOM alone. This is the most common production pairing. Alpine.js, Stimulus, and React+ReactDOM sizes via Bundlephobia.

Real-world migration data

There are no controlled benchmarks comparing HTMX+SSR to React SPA on Core Web Vitals in peer-reviewed form. What exists are migration case studies from teams that made the switch.

Contexte (French news platform, React → Django + HTMX):

MetricBeforeAfterChange
Codebase21,500 LOC7,200 LOC−67%
JS dependencies255 packages9 packages−96%
Build time40 seconds5 seconds−88%
First-load TTI2–6 seconds1–2 seconds−50–60%
Memory usage75 MB45 MB−46%

The authors explicitly caution: “We would not expect every web application to see these sorts of numbers.” Their app was a React-heavy content site with heavy data sets — a strong-fit migration.

OpenUnited (React → HTMX):

MetricChange
Codebase−61% (31,237 → 12,044 LOC)
Total files−72% (588 → 163)
Dev velocity≥5× faster (self-reported)

The architectural trade-off

HTMX has no hydration step. HTML arrives already interactive — there is no interactivity gap by definition. The trade-off: every interaction needs a server round-trip. On high-latency connections or mobile-heavy traffic, that compounds. React with a warm cached bundle can outperform HTMX on repeated interactions once the user is past the first load.

Project health

HTMX is active, not coasting:

The creator has committed to maintaining both HTMX 1.x and 2.x. There is no forced migration timeline.

Decision matrix

HTMX 2Alpine.js 3Stimulus 3React 19
Bundle14 KB13.5 KB10.9 KB44–47 KB
Source of truthServerClientMixedClient
Round-trip per clickYesNoNoNo
SetupOne script tagOne script tagOne script tagBuild toolchain
Offline capableNoNoNoYes
npm last publish2026-04-2120253 years ago2025

Use HTMX 2 when your team is primarily backend developers building CRUD features, admin dashboards, or internal tools. The frontend stays thin; the backend team owns it.

Use HTMX 2 + Alpine.js when you also need client-side reactivity for specific components — date pickers, accordions, real-time counters. The combination covers ~90% of typical web app interaction patterns without a build tool.

Use Stimulus if you are specifically in a Rails/Hotwire shop. The standalone npm package has not been published in three years; it is maintained on GitHub but not relevant outside the Rails ecosystem.

Use React when your UI has complex client state, drag-and-drop, offline requirements, or you are hiring frontend engineers. React skills transfer broadly; HTMX skills do not yet.

Verdict

HTMX 2.0.10 is production-ready. The three v1→v2 breaking changes were well-documented, the htmx-1-compat extension covers incremental migration, and the codebase reached 100% test coverage at 2.0.5. The stability record since then is clean.

The strongest argument for HTMX 2 is not the bundle size — it is the reduction in moving parts. The Contexte numbers (67% code reduction, 96% dependency reduction, 50–60% faster first load) are representative of what happens when you stop treating a content site like a React SPA.

Pick HTMX 2 for CRUD apps, admin interfaces, and content sites with a backend team. Pick React for complex client state, offline, or a large hiring pool. Pick HTMX + Alpine.js when you want both and neither at full weight.

Caveats

File uploads are awkward. This is the most consistently cited pain point across HN threads. The typical workaround is Dropzone.js or a similar library alongside HTMX.

Every interaction needs the server. On high-latency or mobile-heavy setups this is a real ceiling. Not a fit for offline-first requirements.

Job market is thin. React job postings vastly outnumber HTMX. Learning HTMX improves productivity on your current project; it does not improve your resume in most markets yet.

Case study numbers are self-reported. The Contexte and OpenUnited migration data came from the teams that did the migrations. They are informative but not controlled experiments.

No affiliate relationships. HTMX is open-source with no referral program.

References