On the Fence: My Affair with HTMX and the Enduring Allure of React
After building with React for years, my recent dive into HTMX and AlpineJS has been a masterclass in the trade-offs between radical simplicity and creative freedom.
As a full-stack developer who has spent years in the trenches with React, I’ve grown accustomed to its rhythms. Building a separate API backend and a sophisticated front-end client felt like the “right” way to do things. But recently, I decided to step out of my comfort zone. I’ve been building a few projects (one of which is ClaroHQ) with a different kind of stack: HTMX and AlpineJS. And I have to be honest, I’m on the fence.
The Honeymoon Phase: A Breath of Fresh Air
My initial foray into HTMX and AlpineJS was nothing short of a revelation. The biggest draw? Simplicity. Suddenly, I was back to a single, unified codebase. The convoluted dance of managing state on the client, fetching data from an API, and synchronizing everything felt like a distant memory.
With HTMX, the server is king again. It renders HTML, and the front-end’s main job is to smartly swap pieces of that HTML in and out. The data flow is incredibly direct. All the application state lives on the backend, which feels cleaner and less prone to the desynchronization bugs that can plague complex React apps.
Let’s take a super simple example: a “like” button.
With HTMX, it’s almost laughably easy:
<button hx-post="/posts/123/like" hx-target="#like-count" hx-swap="outerHTML">
Like
</button>
<span id="like-count">0 Likes</span>
One click, a POST request goes to the server, the server does its magic, and sends back a new HTML snippet for the like count. HTMX swaps it. Done. No useState
, no useEffect
, no API client configuration. It just works.
The Reality Check: Cracks in the Simple Facade
But as with any honeymoon, eventually, reality sets in. While I adore the simplicity for straightforward tasks, I started hitting the compromises you don’t see in the “Hello World” examples.
My biggest challenge has been updating multiple, unrelated parts of the page at once. In the React world, a tool like Tanstack Query makes this a breeze. You mutate some data, invalidate a query, and everything that depends on that data automatically refetches and updates. It’s a declarative, data-first approach.
With HTMX, it feels more imperative and, at times, a bit clunky. Say you have a form to create a blog post. In that form, you have a dropdown for categories, but also a button to “Create a New Category” on the fly. With HTMX, I can swap in a category creation form. When that form is submitted, the server creates the category. But then what? I need to update the original category dropdown. I could have the server send back the entire, updated select menu and swap it. Or I could use an “out-of-band” swap. Or I could have the server dispatch a custom event that another element is listening for to trigger its own refresh.
All of these work, but they feel like I’m manually wiring things together. It makes me miss the elegant “refetch” magic of Tanstack Query. This also leads to writing a lot more small, single-purpose views on the backend to handle all these specific HTML swaps.
Then there’s the scalability of the approach itself. In a large application, the sheer number of hx-*
attributes and custom JavaScript events fired by AlpineJS can become a web that’s difficult to untangle. Debugging why a certain part of the page updated unexpectedly can mean tracing a chain of events and server responses, which is a different kind of mental overhead than tracing state and props in a React component tree.
The Creative Ceiling: Where React’s Power Still Shines
This brings me to my biggest hesitation. For all its initial complexity, the React ecosystem gives you an incredible amount of creative freedom and power, especially for building complex, application-like user interfaces.
Imagine building a highly interactive analytics dashboard. You’ve got date range pickers, multiple charts that update in real-time, draggable widgets, and complex filtering logic.
Building this in React feels natural. I can break the UI into logical, reusable components (DatePicker
, Chart
, DataGrid
). Each component manages its own state, and a global state manager handles the overarching application state. The virtual DOM efficiently handles the massive number of updates. It’s a paradigm designed for this level of complexity.
Trying to orchestrate this same UI with HTMX feels daunting. While possible, it would involve a complex symphony of server-rendered partials and event triggers. The fine-grained control and rich ecosystem of UI components available for React are hard to give up when the UI demands become truly sophisticated.
This isn’t just a theoretical problem for me. It’s a practical one. If you sign up for my own project, ClaroHQ, you might notice that the UI is intentionally streamlined. That’s partly a product of the HTMX and AlpineJS stack, which has made some of the more complex, interactive features I’ve envisioned challenging to implement. Those features are currently piling up in my backlog, and I’ve been seriously pondering if a front-end rewrite in React is the answer. Modern AI tools would certainly accelerate that process, but it’s the architectural freedom of React I’m really after.
Conclusion: The Right Tool for the Right Job
After spending significant time with both approaches, I’ve come to a familiar conclusion: it’s not about which one is better, but which one is a better fit for the project at hand.
HTMX and AlpineJS are a fantastic choice for server-rendered applications where you want to add dynamic interactivity without the overhead of a full-blown front-end framework. For content-heavy sites, simple CRUD apps, or projects where rapid development is key, this stack is a powerhouse of productivity.
However, for complex, state-heavy single-page applications that feel more like desktop software, React’s structured, component-based architecture is still my go-to. Its ecosystem is vast, and its ability to handle intricate UI interactions is unparalleled.
My journey into the world of HTMX hasn’t made me abandon React. Instead, it has given me a new perspective and another powerful tool in my arsenal. It’s a valuable reminder that in web development, the “best” stack is the one that best solves the problem you’re facing right now. And I’m no longer on the fence about that.
💡 Need a Developer Who Gets It Done?
If this post helped solve your problem, imagine what we could build together! I'm a full-stack developer with expertise in Python, Django, Typescript, and modern web technologies. I specialize in turning complex ideas into clean, efficient solutions.