There’s a specific type of web application that makes modern frontend tooling feel like overkill: the internal tool, the admin dashboard, the CRUD-heavy business app. You need some interactivity — search filtering, inline editing, maybe a modal — but not enough to justify setting up a React project with TypeScript, a state management library, an API layer, and a deployment pipeline that’s more complex than the application itself.

HTMX has carved out a real niche here. The pitch is simple: extend HTML with a few attributes, let the server return HTML fragments instead of JSON, and get SPA-like interactivity without writing a line of JavaScript beyond what HTMX already handles. If you’re a Python, Go, Ruby, or Java developer who’s comfortable with server-side rendering and wants to add interactivity without switching to a JavaScript-first workflow, HTMX 2 (released in 2024, now mature and production-stable) is worth understanding properly.

The Core Idea

HTMX’s model is based on the original web architecture, just extended. In a traditional web application, clicking a link navigates to a new page. HTMX adds attributes like hx-get, hx-post, and hx-swap that let you trigger HTTP requests from any element and replace part of the page with the response — without a full page reload.

Here’s what a basic example looks like:

<button hx-get="/api/users" hx-target="#user-list" hx-swap="innerHTML">
  Load Users
</button>

<div id="user-list"></div>

Click the button, and HTMX sends a GET request to /api/users. Your server returns an HTML fragment — a <ul> of users, say — and HTMX swaps it into #user-list. That’s it. No JavaScript written, no JSON parsing, no state management. The server handles the logic, returns HTML, and HTMX handles the DOM update.

The server-side code is whatever you’d write normally. In FastAPI:

@app.get("/api/users")
async def get_users(request: Request):
    users = db.query(User).all()
    return templates.TemplateResponse("partials/users.html", {"users": users})

You return an HTML partial, not JSON. Your template engine (Jinja2, Go’s html/template, ERB, whatever you use) renders the fragment. HTMX injects it into the page. The mental model stays server-centric.

What HTMX 2 Changed

HTMX 1.x was a single script file you dropped in with a CDN link, and it worked. HTMX 2 (2024) cleaned up the API, removed some legacy behaviour that caused confusion, and improved the extension system. The main breaking changes from 1.x:

  • Removed the hx-on shorthand syntax that conflicted with some browsers
  • Changed the default hx-swap to use settle delays that are more animation-friendly
  • Extensions are now loaded separately rather than bundled — if you use the SSE or WebSocket extensions, you need to import them explicitly

For new projects, you start with HTMX 2. The API is the same shape as 1.x, just cleaner. Migration from 1.x is usually straightforward.

Where It Actually Shines

Admin dashboards and internal tools: This is the sweet spot. An admin UI that needs search filtering, bulk actions, inline status changes, and modal dialogs — but doesn’t need a mobile-first responsive design or complex client-side state — is a near-perfect fit. Django, Flask, FastAPI, Rails, Phoenix, or Go with HTMX and a CSS framework like Tailwind gives you a functional admin panel with minimal JavaScript complexity.

Progressive enhancement on server-rendered apps: If you have an existing server-rendered application and want to add interactivity without a full rewrite, HTMX lets you do it incrementally. Add hx-get to a search box, add hx-post to a form to avoid a full page reload, add hx-swap to a table to enable live filtering — each change is isolated and doesn’t require touching the rest of the application.

APIs for human consumption: HTMX subtly encourages an API design that makes sense for UI interaction — endpoints that return rendered HTML for specific UI fragments, at the granularity the interface needs. This is different from building a REST or GraphQL API that returns raw data, and it’s often faster to build and easier to iterate on for applications where the only consumer is your own UI.

Where It Doesn’t Work

To be honest, there are clear boundaries where HTMX becomes awkward.

Rich client-side state: Drag-and-drop, complex multi-step forms with branching logic, real-time collaborative editing, interactive charts — anything where significant state needs to live and be managed in the browser doesn’t fit the HTMX model well. You either end up sprinking in Alpine.js or Vanilla JS for the complex bits (which works, but defeats some of the simplicity argument) or fighting against the grain of the tool.

Mobile applications: HTMX is a web-only approach. If your project needs a React Native or native mobile app alongside the web UI, you’re going to want a proper API anyway, and the argument for HTMX on the web side weakens.

Large teams with specialised roles: If you have dedicated frontend engineers who know React deeply, the productivity argument for HTMX inverts. Asking a React-fluent team to switch to HTMX creates friction rather than removing it.

Pairing HTMX With Backend Frameworks

Python (FastAPI or Django): Both have good HTMX integration patterns. Django has django-htmx which adds request.htmx detection (so you can return a partial or a full page depending on whether the request is from HTMX). FastAPI works naturally with Jinja2 templates and partial rendering.

Go: Go’s html/template package renders HTML server-side with no external dependency. HTMX pairs cleanly with Go backends — the approach of returning small HTML fragments from HTTP handlers fits Go’s explicit, no-magic style well.

Ruby on Rails: Rails added Turbo (which shares philosophical DNA with HTMX but is Rails-specific and more opinionated) but HTMX works fine with Rails too if you prefer the simpler attribute-based approach without the Turbo frame model.

The Honest Assessment

HTMX isn’t going to replace React for applications that genuinely need React. But it’s addressed something real: the mismatch between the complexity of modern JavaScript tooling and the simplicity of many actual web applications.

For backend developers who want dynamic interfaces without becoming JavaScript experts, or for teams that value tight server-side control and don’t need sophisticated client-side behaviour, HTMX 2 is a productive, maintainable choice. The learning curve is minimal — if you understand HTML and HTTP, you understand HTMX within an afternoon.

The ecosystem around it has also matured. Component libraries (DaisyUI, Shoelace) pair well with it, and the pattern of “server-rendered HTML + HTMX for interactivity + Tailwind for styling” has become a recognisable stack with plenty of reference implementations.

Whether it’s right for your project comes down to one question: is the interactivity you need simple enough to express as partial page replacements triggered by HTTP requests? If yes, HTMX is probably the simplest way to build it.