The HTMX Audit

I was recently asked to review a production web application built over 18 months if I have anything to save their soul. I specifically asked to audit a thriving internal web app started as a prototype in 2023 built by a team enamored with HTMX. This is a small-sized SaaS company focused on hospitality industry, a team of 4 developers with mostly backend-focused engineers, what posibly go wrong you ask? They ditched React, promised simplicity, and believed they’d found web nirvana. Spoiler: they didn’t—at least not at scale.

Chapter 1: Performance Challenges

I opened the main dashboard template (dashboard.html.jinja) and saw a page that dynamically loads every section at once. Each component—reservation cards, booking tables, guest management collapsibles—was set up with hx-* attributes. By the time the page finished loading, Chrome DevTools showed it had dumped almost 2MB of markup and the DOM had ballooned to over 4,000 nodes. Page load times were averaging 3.8 seconds on their staging environment.

They didn’t use pagination. Everything was inline. And every interaction meant partial swaps of large DOM trees, causing noticeable lag during peak usage hours.

Chapter 2: Maintainability Concerns

During my code review process, I found a wild mix of:

  • Utility-heavy CSS classes
  • Server-side templating conditionals
  • HTMX attributes crammed into almost every tag
  • Bits of inline script for glue logic

It wasn’t clear where layout ended and logic began. Some buttons swapped entire sections of the page (like #reservation-panel), but others swapped just the component (#guest-form-container). But you’d only know by digging into the HTML soup.

One dev jokingly said:

“Every time I open a template, it feels like decoding an alien script.”

It wasn’t even clear what was supposed to be handled by the server or the client anymore.

Chapter 3: Separation of Concerns… What?

Backend routes were returning different things depending on the request: HTML when HTMX was involved, JSON when a native fetch was expected. In a few cases, the same Flask controller (/api/reservations) had three different response types depending on headers.

Want to add a mobile version? Good luck rewriting all that logic again. There was no clean API contract—just responses baked into Jinja2 templates.

This kind of blending of responsibilities works fine at first, but it makes long-term reuse and testing much harder.

Chapter 4: State Is in the DOM, and That’s Bad

Things like modals, form state, dropdowns, and even alert banners were managed through DOM state only. If the user interacted mid-load, the swaps would often reset their state.

A user clicked “Edit”, typed into the reservation form, then a background auto-refresh wiped their changes. Classic HTMX hazard if you’re not tracking state somewhere persistent.

Chapter 5: Debugging Difficulties

One attribute typo and nothing happens—no logs, no errors, no hints. Using browser performance profiling and manual testing, I discovered several silent failures.

In this codebase, several bugs were just silent failures: wrong IDs in hx-target (like targeting #booking-list instead of #bookings-list), missing triggers, or event attributes not being picked up. They were running HTMX 1.8.4 with no linters, no validation tools, and the browser console provided minimal debugging information.

HTMX offers a lot in terms of simplicity, but once you go off the paved path, it’s easy to feel like you’re debugging blindfolded.

At a Glance: Audit Findings

CategoryWhat Went Wrong
PerformanceLarge DOM swaps on every interaction caused slowdowns
MaintainabilitySpaghetti markup with logic, layout, and styling entangled
Separation of LogicControllers returned different formats with no contract
State ManagementUI state was reset due to DOM replacement
Tooling & DebuggingNo linting, no validation, and no meaningful error output

Final Thoughts

This isn’t about HTMX being inadequate—it’s about architectural fit. HTMX excels at what it was designed for: bringing dynamic behavior to server-rendered applications with minimal JavaScript complexity. For rapid prototyping, content-heavy sites, and straightforward CRUD applications, it’s genuinely excellent.

The challenges we observed here weren’t failures of the technology, but rather natural consequences of using any tool beyond its optimal scope. Every framework and library has a scaling threshold where its core assumptions start working against you rather than for you.

The team made reasonable choices given their goals: faster development, reduced JavaScript complexity, and leveraging server-side strengths. These are valid architectural priorities. What happened is common in software development—a solution that works beautifully at one scale encounters different trade-offs as requirements evolve.

HTMX remains a solid choice for many projects. This audit clearly illustrates that as applications grow in complexity, state management needs, and interactive requirements, the fundamental architectural decisions become more critical. The “HTML over the wire” approach that makes HTMX so appealing for simpler use cases can become a constraint when building more sophisticated user experiences.

What Could Be Done About It

The Quick Reality Check:

Facing these issues, the natural instinct is to consider a complete rewrite. A full migration to React or Vue would take this 4-person backend team 12-18 months to reach feature parity—that’s over a year of zero new customer features while competitors advance.

The Pragmatic Approach:

Instead of burning everything down, the realistic path forward involves:

  • Immediate fixes (1-2 months): Address the most painful user-facing issues—fix form data loss, add basic pagination to slow queries, implement simple loading states. Keep the revenue flowing while stopping the bleeding.
  • Strategic replacement: Start building a modern replacement system in parallel, beginning with new features or less critical components. Test the new architecture on a subset of users while the existing system continues serving customers.

The Real Problem:

The technical issues are symptoms of a deeper challenge—lack of senior technical leadership. This team needs an experienced architect who can establish patterns, enforce conventions, and provide strategic direction. Sometimes the missing piece isn’t better technology, but better decision-making.

Investment Reality:

These four combined developers cost the same as one senior architect who could prevent these problems from occurring. The math is clear—structure and leadership often matter more than the underlying technology stack.

The lesson:

Don’t let perfect be the enemy of profitable. Fix what’s broken, build what’s next, but never stop serving customers while you’re figuring it out.

Guidance for Technology Selection

When evaluating HTMX (or any architectural approach), consider these project characteristics:

HTMX works exceptionally well when:

  • Most interactions are form submissions or content updates
  • User workflows are relatively linear
  • Server-side rendering aligns with your team’s expertise
  • Development speed is prioritized over complex interactivity
  • The application is primarily content-focused rather than app-like

Consider additional architectural patterns when:

  • Users expect rich, stateful interactions (complex forms, real-time collaboration)
  • You need fine-grained performance optimization
  • Multiple client types (mobile apps, APIs) will consume the same data
  • The UI requires significant client-side state coordination
  • Team members have strong front-end framework expertise

The goal isn’t to choose the “best” technology, but to match your architectural approach to your project’s actual requirements and constraints. HTMX can be the right answer—just make sure it’s the right question.

HTMX can be a strong ally. But remember: simplicity is a valid strategy—until it’s not sustainable. Sometimes too much simplicity becomes the very thing that breaks you.