HTMX: Xây dựng Web động ‘siêu tốc’ mà không cần cài React hay Vue

Development tutorial - IT technology blog
Development tutorial - IT technology blog

Đừng để JavaScript làm phức tạp hóa dự án của bạn

Bạn chỉ muốn làm một nút “Like” không cần load trang hay một bộ lọc tìm kiếm realtime? Đừng vội cài React hay Vue. Thực tế, nhiều dự án nhỏ hiện nay đang phải gánh tới 300MB node_modules chỉ để render vài dòng văn bản. Việc cấu hình Webpack hay Vite đôi khi còn tốn thời gian hơn cả việc viết logic nghiệp vụ.

Tôi từng mất cả buổi sáng chỉ để đồng bộ trạng thái (state) giữa Backend Python và Frontend React cho một trang dashboard nội bộ. Logic bị chia cắt ở hai nơi khiến việc bảo trì trở nên cực kỳ khó khăn. Chúng ta đang lạm dụng mô hình Single Page Application (SPA) cho những tác vụ vốn dĩ rất đơn giản.

HTMX xuất hiện như một giải pháp thay thế gọn nhẹ. Nó giúp bạn đưa mọi thứ trở về với bản chất nguyên thủy của Web nhưng vẫn đảm bảo trải nghiệm mượt mà.

HTMX là gì?

HTMX không phải là một framework đồ sộ. Đây là một thư viện siêu nhẹ, chỉ khoảng 14KB (gzipped). Nó cho phép bạn truy cập trực tiếp vào các tính năng hiện đại của trình duyệt thông qua các thuộc tính HTML, thay vì phải viết code JavaScript thuần hay dùng thư viện ngoài.

Thông thường, chỉ thẻ <a><form> mới có thể gửi request HTTP. HTMX phá vỡ giới hạn đó. Giờ đây, bất kỳ phần tử nào như div, button hay input đều có thể gửi request GET, POST, PUT, DELETE. Thay vì nhận về JSON thô, server sẽ trả về một đoạn mã HTML (HTML fragment). HTMX sẽ tự động chèn đoạn mã này vào vị trí bạn chỉ định.

Sức mạnh của mô hình Hypermedia-driven Development (HDD)

Hãy thử thay đổi góc nhìn. Trong mô hình React truyền thống, server chỉ là “người giao hàng” trả về dữ liệu JSON. Với HTMX, server đóng vai trò là “kiến trúc sư” trực tiếp tạo ra giao diện.

Khi áp dụng HDD, toàn bộ logic nghiệp vụ sẽ tập trung tại Backend. Trình duyệt chỉ cần hiển thị những gì server gửi về. Cách tiếp cận này loại bỏ hoàn toàn bước chuyển đổi JSON (serialize/deserialize) phức tạp. Bạn sẽ tiết kiệm được đáng kể thời gian phát triển và giảm thiểu lỗi đồng bộ.

Nếu bạn cần xử lý dữ liệu nhanh trong quá trình code backend, hãy thử các công cụ tại toolcraft.app. Tôi thường dùng nó để format JSON hoặc test Regex nhanh mà không cần mở thêm extension nặng nề.

Thực hành: Tích hợp HTMX vào Flask với 0 dòng JS

Chúng ta sẽ xây dựng một ứng dụng Todo List đơn giản. Tính năng thêm mới sẽ hoạt động mượt mà mà không cần tải lại toàn bộ trang web.

Cài đặt môi trường Flask:

pip install Flask

Viết logic cho file app.py:

from flask import Flask, render_template, request

app = Flask(__name__)
todos = ["Học HTMX", "Viết bài cho itfromzero"]

@app.route("/")
def index():
    return render_template("index.html", todos=todos)

@app.route("/add-todo", methods=["POST"])
def add_todo():
    new_todo = request.form.get("todo")
    if new_todo:
        todos.append(new_todo)
    # Chỉ trả về item mới dưới dạng HTML
    return f"<li>{new_todo}</li>"

if __name__ == "__main__":
    app.run(debug=True)

Cấu hình template index.html:

<script src="https://unpkg.com/[email protected]"></script>

<h1>Danh sách công việc</h1>

<form hx-post="/add-todo" hx-target="#todo-list" hx-swap="beforeend" hx-on::after-request="this.reset()">
    <input type="text" name="todo" placeholder="Thêm việc mới...">
    <button type="submit">Thêm</button>
</form>

<ul id="todo-list">
    {% for todo in todos %}
        <li>{{ todo }}</li>
    {% endfor %}
</ul>

Ba thuộc tính quan trọng bạn cần nhớ:

  • hx-post: Gửi request POST ngay khi submit form.
  • hx-target: Chỉ định nơi sẽ nhận nội dung mới từ server.
  • hx-swap="beforeend": Chèn nội dung mới vào cuối danh sách thay vì thay thế toàn bộ.

Kết hợp FastAPI và HTMX để tối ưu hiệu năng

FastAPI với khả năng xử lý async sẽ là cặp bài trùng hoàn hảo cho HTMX. Sự kết hợp này giúp giảm độ trễ (latency) đáng kể so với việc chờ Client-side render.

from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def read_item(request: Request):
    return templates.TemplateResponse("index.html", {"request": request, "items": ["FastAPI", "HTMX"]})

@app.post("/search", response_class=HTMLResponse)
async def search(request: Request, q: str = Form(...)):
    results = [item for item in ["Python", "JavaScript", "C++"] if q.lower() in item.lower()]
    return templates.TemplateResponse("partials/results.html", {"request": request, "results": results})

Bí quyết ở đây là sử dụng “partials” template. Bạn chỉ render một phần nhỏ của giao diện khi có request từ HTMX. Phương pháp này giúp tiết kiệm băng thông và giảm tải cho CPU của server.

Lưu ý quan trọng từ trải nghiệm thực tế

Sau khi triển khai HTMX cho một số hệ thống quản trị, tôi rút ra 4 bài học chính:

  1. Chọn đúng công cụ: HTMX không sinh ra để làm các ứng dụng như Google Maps hay Photoshop Online. Nó mạnh nhất ở các trang Blog, Dashboard hoặc E-commerce.
  2. Tận dụng hx-trigger: Bạn có thể tạo tính năng Live Search chỉ với một thuộc tính. Hãy dùng keyup changed delay:500ms để tránh gửi quá nhiều request lên server.
  3. An toàn dữ liệu: Luôn escape dữ liệu để phòng tránh lỗi XSS. Các template engine như Jinja2 thường tự động xử lý việc này, nhưng bạn vẫn nên kiểm tra kỹ.
  4. Kiểm tra lỗi: Hãy mở tab Network trong DevTools để theo dõi. Bạn sẽ thấy các request HTMX rất tường minh với nội dung HTML trả về trực tiếp.

Lời kết

HTMX không thay thế hoàn toàn các framework JavaScript. Tuy nhiên, nó giúp các Backend Developer tự tin xây dựng ứng dụng tương tác cao mà không cần sa lầy vào hệ sinh thái JS thay đổi chóng mặt. Nếu bạn đang làm dự án cá nhân hay công cụ nội bộ, hãy thử cài HTMX. Đôi khi, sự đơn giản chính là chìa khóa của hiệu quả.

Share: