HTMX is a small JavaScript library that extends HTML. Instead of writing JavaScript to make AJAX requests and update the DOM, you add attributes to your HTML elements and HTMX handles the rest.
Key Takeaways
- HTMX lets any HTML element make HTTP requests and swap page content — no JavaScript required
- Your server returns HTML fragments, not JSON — simpler architecture for server-rendered apps
- HTMX is ~14KB; React + ReactDOM is ~160KB — a 10x size difference that matters for first-load performance
- HTMX shines with Python, Go, Rails, and PHP backends — technologies that already render HTML well
- React is still the right choice for complex client-side state, real-time collaboration, and highly interactive UIs
What Is HTMX?
HTMX is a small JavaScript library that extends HTML. Instead of writing JavaScript to make AJAX requests and update the DOM, you add attributes to your HTML elements and HTMX handles the rest.
The idea behind HTMX is called "hypermedia." In the original web, clicking a link made the browser request a new full HTML page from the server. HTMX brings that same simplicity to partial page updates — the server still returns HTML, but instead of replacing the entire page, HTMX swaps just the part that changed.
This makes HTMX particularly powerful if you're using a backend that already renders HTML — Django templates, Go's html/template, ERB in Rails, Blade in Laravel. You don't need to rebuild your backend as a JSON API. You just change some routes to return HTML fragments.
Core Attributes
HTMX adds six core attributes to HTML:
hx-get and hx-post
Make HTTP requests when an element is triggered.
<!-- GET request to /search when button is clicked --> <button hx-get="/search" hx-target="#results"> Search </button> <!-- POST request with form data --> <form hx-post="/submit"> <input name="email" type="email"> <button type="submit">Subscribe</button> </form>
There's also hx-put, hx-patch, and hx-delete for full REST support.
hx-target
Specifies which element on the page should receive the response HTML. Uses CSS selector syntax:
<button hx-get="/user/42" hx-target="#user-card"> Load User </button> <div id="user-card"></div>
When clicked, the button requests /user/42, and the server's HTML response gets placed inside #user-card.
Swapping Content with hx-swap
hx-swap controls how the response replaces the target:
innerHTML(default) — replaces the inner content of the target elementouterHTML— replaces the target element itselfbeforebegin— inserts before the target elementafterend— inserts after the target elementprepend— inserts inside the target, before existing contentappend— inserts inside the target, after existing contentdelete— removes the target element (ignores the response)
<!-- Append new items to a list --> <button hx-get="/more-items" hx-target="#list" hx-swap="beforeend"> Load More </button> <ul id="list">...existing items...</ul>
Triggers and Events
By default, HTMX triggers on the natural event for each element — click for buttons, submit for forms, change for inputs. The hx-trigger attribute overrides this.
<!-- Trigger on input (with 500ms delay for debouncing) --> <input hx-get="/search" hx-trigger="keyup changed delay:500ms" hx-target="#results" name="q" placeholder="Search..." > <!-- Trigger when element becomes visible (for infinite scroll) --> <div hx-get="/next-page" hx-trigger="revealed" hx-swap="afterend"> Loading... </div>
Polling
<!-- Refresh this div every 5 seconds --> <div hx-get="/live-status" hx-trigger="every 5s"> Checking status... </div>
Real Example: Live Search
Here's a complete live search implementation. Your HTML:
<input hx-get="/search" hx-trigger="keyup changed delay:300ms" hx-target="#search-results" name="query" placeholder="Search products..." > <div id="search-results"></div>
Your server endpoint (Python/Flask example):
@app.route('/search')
def search():
query = request.args.get('query', '')
results = Product.query.filter(
Product.name.ilike(f'%{query}%')
).limit(10).all()
return render_template('_search_results.html', results=results)
Your _search_results.html template:
{% for product in results %}
<div class="result-item">
<strong>{{ product.name }}</strong>
<span>${{ product.price }}</span>
</div>
{% else %}
<p>No results found.</p>
{% endfor %}
That's a fully functional live search with debouncing. No JavaScript written.
HTMX with Different Backends
HTMX works with any backend that returns HTML. Community libraries have added HTMX-aware helpers for most frameworks:
- Python/Django —
django-htmxadds request.htmx detection and response helpers - Python/Flask — no special library needed; just render templates and return them
- Go — use
html/templateto render partials directly - Ruby on Rails — HTMX works naturally with Turbo/Hotwire or standalone
- Node.js/Express — render EJS or Handlebars partials in route handlers
When to Use HTMX vs React
The honest answer is that these aren't competing in the same space most of the time.
Use HTMX when:
- You're building on a server-rendered backend (Python, Go, Rails, PHP)
- Your UI interactions are relatively simple — form submissions, content loading, filtering
- SEO matters and you want server-rendered HTML as the baseline
- You want to add interactivity without committing to a JavaScript build pipeline
- You're a solo developer or small team who wants to move fast without frontend complexity
Use React when:
- You're building a highly interactive SPA — real-time collaboration, complex editors, dashboards with lots of client state
- Your team is already deep in JavaScript and has React expertise
- You need fine-grained control over animations and transitions
- The backend is a pure API and there's no server rendering
HTMX Limitations
HTMX isn't right for everything:
- Complex client state — if you have lots of client-side state that doesn't map to server requests, HTMX gets awkward
- Real-time two-way data — WebSocket support in HTMX exists (
hx-ws) but is less mature than dedicated solutions - Rich animations — CSS transitions work, but complex JS-driven animations are harder to manage
- Mobile apps — HTMX is a web-only library; React Native or Expo are needed for native mobile
- Team size — on large teams where frontend and backend engineers are separate, the shared HTML template approach can create friction
Learn Modern Web Development at Our AI Bootcamp
We cover frontend, backend, deployment, and AI integration — hands-on over 3 days. You'll know how to pick the right tool for the right job.
Upcoming bootcamps: Denver • New York City • Dallas • Los Angeles • Chicago
View Dates and EnrollFAQ
What is HTMX?
HTMX is a JavaScript library that lets you add interactivity to HTML pages using HTML attributes instead of JavaScript code. You can make AJAX requests, swap content on the page, and handle events — all by adding attributes like hx-get, hx-post, and hx-swap to regular HTML elements.
Is HTMX better than React?
They solve different problems. React is a component framework for complex, stateful UIs where client-side state management matters — dashboards, real-time apps, rich editors. HTMX is ideal for server-rendered apps where you want interactivity without rebuilding your backend as a JSON API. If you're using Python, Go, Ruby, or PHP on the backend, HTMX is often the simpler choice.
Does HTMX require a specific backend?
No. HTMX works with any backend that can return HTML — Python/Django, Go, Ruby on Rails, PHP, Node.js/Express. Instead of returning JSON, your server returns HTML fragments that HTMX swaps into the page.
How large is HTMX?
HTMX is approximately 14KB minified and gzipped — significantly smaller than React (~40KB) plus React DOM (~120KB). For content-heavy sites where bundle size matters, this is a real advantage.