· htmx / htmx-2 / review

Đánh giá HTMX 2: thay đổi gì, hỏng gì, và đã sẵn sàng chưa?

HTMX 2.0.10 đã sẵn sàng cho production. Ba breaking change từ v1, escape hatch migration, số liệu hiệu năng thực tế, ma trận quyết định với Alpine.js và React.

Bởi

2.019 từ · 11 phút đọc

Dùng HTMX 2 nếu team bạn là backend-first và đang xây dựng tính năng CRUD, admin dashboard, hay internal tool. Nếu cần state phức tạp phía client, optimistic update, hay hỗ trợ offline, HTMX sẽ không đủ sức — hãy kết hợp với Alpine.js hoặc dùng React.

Bài này dành cho ai

Backend developer (Python, Go, Ruby, Node) đang cân nhắc xem HTMX 2 có phù hợp với dự án tiếp theo không. Bài viết giả định bạn đã biết HTMX là gì. Nếu muốn tìm hiểu lý do kiến trúc của HTMX vs React, xem HTMX vs React 2026. Bài này tập trung cụ thể vào phiên bản v2: thực sự thay đổi gì, cái gì bị hỏng, và dự án có đủ ổn định để cược vào không.

Những gì chúng tôi đánh giá về HTMX 2

[email protected], phát hành 2026-04-21.

Nguồn: changelog chính thức, hướng dẫn migration v1 → v2, thread Hacker News về HTMX 2.0.0, hai case study migration đã công bố, và State of JS 2024.

Ba thay đổi breaking quan trọng

Từ HTMX 1.x lên 2.0 không phải là một nâng cấp thay thế trực tiếp. Ba thay đổi sau đây sẽ làm hỏng code đang hoạt động.

DELETE request giờ dùng URL parameter

HTMX 1.x gửi payload DELETE trong phần body của request. Phiên bản 2 chuyển chúng sang URL parameter để tuân thủ đặc tả HTTP. Nếu API của bạn đọc DELETE body, thêm đoạn này trước khi tải htmx:

htmx.config.methodsThatUseUrlParams = ["get"]  // khôi phục hành vi v1
// mặc định v2: ["get", "delete"]

Extension đã bị tách riêng

Tất cả extension — SSE, WebSockets, preload, idiomorph, head-support — đã bị loại khỏi bản phân phối chính và giờ nằm tại extensions.htmx.org.

Extension SSE là thay đổi gây hỏng hoàn toàn. Extension SSE của v1 sẽ không hoạt động với htmx 2. Bạn phải nâng cấp extension đó riêng.

Theo bài đăng phát hành chính thức: “Tất cả extension đã được chuyển ra khỏi core repository sang repo và website riêng.”

Cú pháp hx-on dạng attribute đơn đã bị xóa

Cũ (v1):

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

Mới (v2):

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

Dạng attribute đơn đã bị loại bỏ, thay bằng cú pháp hx-on:event-name. Lưu ý dạng kebab-case trong tên attribute (htmx:before-request, không phải htmx:beforeRequest). Đây là migration tìm và thay.

Giá trị mặc định config thay đổi âm thầm

Ba giá trị mặc định đã thay đổi trong v2:

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

Thay đổi selfRequestsOnly: true có ý nghĩa bảo mật quan trọng nhất: AJAX cross-domain bị chặn theo mặc định trong v2. Nếu bạn đang dựa vào cross-origin request, bạn sẽ cần tắt tùy chọn này một cách tường minh.

Escape hatch cho việc migration

Với các team không thể migrate tất cả cùng một lúc, extension htmx-1-compat khôi phục hành vi v1 chỉ với một dòng:

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

Extension này bao gồm: cú pháp hx-on cũ, smooth scroll, DELETE form body, cross-domain request, và các attribute hx-ws/hx-sse cũ. Nó không thể khôi phục hỗ trợ IE11 — cái đó đã bị bỏ vĩnh viễn.

Tính năng mới trong v2

hx-disabled-elt: Vô hiệu hóa các element cụ thể trong khi request đang chạy, không chỉ element kích hoạt.

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

Inline event dạng wildcard hx-on:: Xử lý các sự kiện lifecycle của htmx trực tiếp, không cần block <script>.

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

View Transitions API: Dùng transition:true trong hx-swap để cập nhật DOM có animation, hoặc bật toàn cục với htmx.config.globalViewTransitions = true.

Config responseHandling: Một mảng handler cho HTTP status code có thể cấu hình. Bạn giờ có thể kiểm soát code nào kích hoạt swap và code nào kích hoạt xử lý lỗi — thay thế cho hành vi cũ chỉ nhận 200.

Mặc định bảo mật tốt hơn: Các tùy chọn config inlineScriptNonceinlineStyleNonce thêm hỗ trợ CSP nonce.

DX trong thực tế

Đây là giao diện validation form inline trong HTMX 2 với Django — dùng hai tính năng chỉ có trong v2:

# 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>

hx-disabled-elt="this" vô hiệu hóa input trong khi đang validate. Các handler hx-on: thêm và xóa CSS class mà không cần thẻ <script>. Response là một HTML fragment đơn giản. Không JSON, không client-side rendering, không state management.

Phản hồi từ thread HN 2.0.0 cho thấy rõ sự thay đổi trải nghiệm: “Với khoảng 5 attribute htmx, tôi đã xóa được khoảng 500 dòng JavaScript phía client. Một trong những ngày code vui nhất trong 10 năm qua.” Một commenter khác với 20 năm kinh nghiệm qua jQuery, Backbone, Ember, React, và Vue: “Tôi thực sự nghĩ htmx là cái duy nhất tôi thích ngay cả sau khi đã xây dựng thứ gì đó tử tế với nó.”

Backend Python? Xem Django vs FastAPI 2026 để chọn framework backend trước khi tích hợp HTMX.

Hiệu năng

Kích thước bundle

~14 KB min+gzipped (GitHub README). Không cần bước build.

Thư việnPhiên bảnMin+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 kết hợp: 27.5 KB — vẫn nhỏ hơn React+ReactDOM đơn lẻ. Đây là cặp kết hợp phổ biến nhất trong production. Kích thước Alpine.js, Stimulus, và React+ReactDOM qua Bundlephobia.

Dữ liệu migration thực tế

Không có benchmark có kiểm soát nào so sánh HTMX+SSR với React SPA trên Core Web Vitals dưới dạng peer-reviewed. Những gì có là các case study migration từ các team đã thực hiện chuyển đổi.

Contexte (nền tảng tin tức Pháp, React → Django + HTMX):

Chỉ sốTrướcSauThay đổi
Codebase21,500 LOC7,200 LOC−67%
JS dependencies255 package9 package−96%
Thời gian build40 giây5 giây−88%
TTI lần tải đầu2–6 giây1–2 giây−50–60%
Mức dùng bộ nhớ75 MB45 MB−46%

Tác giả lưu ý rõ: “Chúng tôi không kỳ vọng mọi ứng dụng web đều đạt được những con số như vậy.” App của họ là một trang tin tức nặng React với bộ dữ liệu lớn — một migration phù hợp theo nhiều tiêu chí.

OpenUnited (React → HTMX):

Chỉ sốThay đổi
Codebase−61% (31,237 → 12,044 LOC)
Tổng số file−72% (588 → 163)
Tốc độ phát triển≥5× nhanh hơn (tự báo cáo)

Đánh đổi kiến trúc

HTMX không có bước hydration. HTML đến đã có tương tác ngay — không có khoảng trống về tính tương tác theo định nghĩa. Đánh đổi: mỗi tương tác cần một vòng server round-trip. Trên kết nối độ trễ cao hoặc traffic nặng từ mobile, điều này tích lũy đáng kể. React với bundle cache nóng có thể vượt HTMX trên các tương tác lặp lại sau khi user đã qua lần tải đầu.

Sức khỏe dự án

HTMX đang phát triển tích cực, không chỉ duy trì:

Tác giả đã cam kết duy trì cả HTMX 1.x và 2.x. Không có lộ trình migration bắt buộc.

Ma trận quyết định

HTMX 2Alpine.js 3Stimulus 3React 19
Bundle14 KB13.5 KB10.9 KB44–47 KB
Nguồn dữ liệu chínhServerClientKết hợpClient
Round-trip mỗi clickKhôngKhôngKhông
Cài đặtMột script tagMột script tagMột script tagBuild toolchain
Hoạt động offlineKhôngKhôngKhông
Lần xuất bản npm gần nhất2026-04-2120253 năm trước2025

Dùng HTMX 2 khi team của bạn chủ yếu là backend developer xây dựng tính năng CRUD, admin dashboard, hay internal tool. Frontend vẫn gọn nhẹ; team backend làm chủ nó.

Dùng HTMX 2 + Alpine.js khi bạn cũng cần reactivity phía client cho các component cụ thể — date picker, accordion, counter thời gian thực. Sự kết hợp này bao phủ ~90% các pattern tương tác điển hình của web app mà không cần build tool. Xem so sánh đầy đủ: Alpine.js vs HTMX.

Dùng Stimulus nếu bạn đang trong một dự án Rails/Hotwire. Package npm standalone chưa được xuất bản trong ba năm; nó vẫn được duy trì trên GitHub nhưng không liên quan ngoài hệ sinh thái Rails.

Dùng React khi UI có state phức tạp phía client, kéo-thả, yêu cầu offline, hoặc khi bạn cần tuyển dụng frontend engineer. Kỹ năng React được chấp nhận rộng rãi; kỹ năng HTMX thì chưa.

Kết luận

HTMX 2.0.10 đã sẵn sàng cho production. Ba thay đổi breaking từ v1→v2 được ghi chép tốt, extension htmx-1-compat hỗ trợ migration từng phần, và codebase đạt 100% test coverage tại phiên bản 2.0.5. Lịch sử ổn định từ đó đến nay sạch sẽ.

Lý do mạnh nhất để chọn HTMX 2 không phải là kích thước bundle — mà là việc giảm số lượng thành phần phải quản lý. Những con số của Contexte (giảm 67% code, giảm 96% dependency, tải nhanh hơn 50–60% lần đầu) là đại diện cho những gì xảy ra khi bạn ngừng coi một trang nội dung là một React SPA.

Chọn HTMX 2 cho ứng dụng CRUD, admin interface, và trang nội dung với team backend. Chọn React khi cần state phức tạp phía client, offline, hay pool tuyển dụng lớn. Chọn HTMX + Alpine.js khi bạn muốn cả hai mà không phải dùng cái nào ở mức tối đa.

Lưu ý

Upload file khá rắc rối. Đây là điểm đau được nhắc đến nhiều nhất trên các thread HN. Giải pháp thường gặp là dùng Dropzone.js hoặc thư viện tương tự bên cạnh HTMX.

Mỗi tương tác cần server. Trên môi trường độ trễ cao hoặc nhiều user mobile, đây là giới hạn thực sự. Không phù hợp với yêu cầu offline-first.

Thị trường việc làm còn hạn chế. Tin tuyển dụng React vẫn áp đảo HTMX. Học HTMX cải thiện năng suất trong dự án hiện tại của bạn; nhưng chưa giúp ích nhiều cho CV trong hầu hết thị trường hiện tại.

Số liệu case study là tự báo cáo. Dữ liệu migration của Contexte và OpenUnited đến từ chính các team thực hiện migration. Đây là thông tin tham khảo hữu ích nhưng không phải thí nghiệm có kiểm soát.

Không có quan hệ affiliate. HTMX là open-source và không có chương trình referral.

Tham khảo