What if you could build interactive web apps using just HTML attributes? No React. No build step. No npm install.
That's htmx.
What Is htmx?
htmx gives any HTML element the ability to make HTTP requests and update the DOM. It extends HTML instead of replacing it.
<script src="https://unpkg.com/htmx.org@2.0.0"></script>
<!-- Click button → GET request → replace #results -->
<button hx-get="/api/users" hx-target="#results">
Load Users
</button>
<div id="results"></div>
That's a complete AJAX interaction. No JavaScript written.
The Core Attributes
<!-- Any element can make requests -->
<button hx-get="/api/data">GET request</button>
<form hx-post="/api/users">POST on submit</form>
<button hx-delete="/api/users/1">DELETE request</button>
<!-- Control what gets updated -->
<button hx-get="/search" hx-target="#results">Search</button>
<button hx-get="/row" hx-swap="afterbegin">Prepend</button>
<button hx-get="/row" hx-swap="outerHTML">Replace self</button>
<!-- Trigger on any event -->
<input hx-get="/search" hx-trigger="keyup changed delay:300ms" name="q">
<div hx-get="/news" hx-trigger="every 30s">Live feed</div>
Real Example: Live Search
<input type="search"
name="q"
hx-get="/search"
hx-trigger="input changed delay:300ms"
hx-target="#search-results"
placeholder="Search users...">
<table>
<tbody id="search-results">
<!-- Results appear here -->
</tbody>
</table>
Server returns HTML fragments, not JSON. Your server template renders the <tr> rows. htmx drops them in.
Why Developers Are Switching
1. No build step — Add one <script> tag. Done.
2. Server renders HTML — Use Django, Rails, Flask, Express, Go — whatever. Return HTML, not JSON.
3. Progressive enhancement — Works without JavaScript (forms still submit).
4. Tiny — 14KB minified+gzipped vs React 42KB + ReactDOM 130KB.
5. Simplicity — A junior developer can be productive in 30 minutes.
htmx + Any Backend
# Flask
@app.route("/search")
def search():
q = request.args.get("q", "")
users = User.query.filter(User.name.contains(q)).all()
return render_template("partials/user_rows.html", users=users)
// Go
func searchHandler(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("q")
users := searchUsers(q)
tmpl.ExecuteTemplate(w, "user_rows", users)
}
The server returns an HTML fragment. htmx handles the swap. That's the whole architecture.
Need data tools or custom web solutions? Check out my scraping toolkit or email spinov001@gmail.com.
Top comments (0)