htmx vs React 2026 — When to Skip the JavaScript Framework

Photo: RealToughCandy.com / Pexels
Most web interfaces answer the same question: when the user does something, who decides what the screen looks like next? React's answer is the browser. Your JavaScript holds the state, computes the new view, and patches the page. htmx's answer is the server. The browser sends a request, the server replies with a chunk of HTML, and htmx drops that chunk into the page. This comparison explains how each model works, where each one breaks down, and how to choose without turning it into a tribal argument.
The short answer: pick by where your complexity lives
The useful question is not "which tool is better" but "where does the hard part of this application live?" If the hard part is on the server (permissions, validation, reports, database rules), htmx lets the server stay in charge and keeps the browser thin. If the hard part is on the client (drag-and-drop, live cursors, offline editing, tight animation), a client-side framework earns its weight.
| Situation | Leans toward | Why |
|---|---|---|
| Admin panels, internal tools, back-office CRUD | htmx | Forms and tables map directly to HTML requests |
| Content sites with some interactive widgets | htmx or server rendering | First render is plain HTML |
| Spreadsheet-like or canvas-like apps | React or another client framework | Many interdependent values change per keystroke |
| Apps that must work offline | React or another client framework | State has to live on the device |
| Team of React specialists with a large component library | React | Switching cost outweighs the gain |
| One or two developers who know Python, Go, or Ruby | htmx | One language, one deploy, one mental model |
Most real products are mixed. A billing dashboard might be 90 percent forms and tables with one interactive chart, and nothing stops you from using each tool where it fits. The htmx project's own guidance says as much: in its essay on when to use hypermedia, it notes that embedding client-side components inside a hypermedia app is usually easier than the reverse (see When To Use Hypermedia?).
What htmx actually is
htmx is a small JavaScript library that lets you issue HTTP requests and update the page using HTML attributes alone. Its homepage describes it as dependency-free and about 16 KB minified and gzipped (htmx.org). The project's documentation sums up the idea in one sentence: htmx lets you "access modern browser features directly from HTML, rather than using javascript" (htmx docs).
Think of it like upgrading a paper form. A traditional form can only do one thing: submit and reload the entire page. htmx gives every element, not just forms, the ability to make a request and say which part of the page should change. The server still speaks HTML, the language browsers already understand. For a deeper tour of the library itself, see our explainer on what HTMX is and how it works.
The core loop in four lines
<!-- Click the button, POST to /click, put the response inside #result -->
<button hx-post="/click" hx-target="#result">Click me</button>
<div id="result"></div>
When the click happens, htmx sends a POST to /click. The server returns an HTML fragment, perhaps <p>Saved at 14:02</p>. htmx swaps it into #result. There is no JSON, no client-side template, and no component state to reconcile.
Triggers and swaps do most of the work
Two ideas cover most of htmx. A trigger says when a request fires, and a swap says how the response lands. The hx-trigger attribute accepts modifiers: changed fires only if the value changed, delay:300ms waits for a pause and resets if the event repeats, throttle limits request rate, revealed fires when an element scrolls into view, and every 10s polls (hx-trigger reference). The hx-swap attribute offers innerHTML (the default), outerHTML, beforeend, afterend, delete, and none, plus modifiers such as view transitions and scroll control (htmx docs).
<!-- Search as you type, but only after a 300 ms pause -->
<input type="search" name="q"
hx-get="/search"
hx-trigger="keyup changed delay:300ms"
hx-target="#results">
<div id="results"></div>
<!-- Infinite scroll: fetch the next page when this row becomes visible -->
<div hx-get="/page/2" hx-trigger="revealed" hx-swap="afterend">
Loading more...
</div>
These two snippets replace what usually takes a debounce hook, a fetch call, a loading state, and a list renderer in a client framework. The tradeoff is that the server now owns the markup for every fragment.
Boosting ordinary links and forms
The hx-boost attribute converts normal anchors and forms into AJAX requests that target the page body by default. Without JavaScript, links and forms still work as plain requests, which the docs call progressive enhancement. A related detail matters for navigation: when you use hx-push-url, htmx stores a DOM snapshot and pushes the URL into history, and any URL you push must return a full page when someone loads it directly (htmx docs). That requirement forces a discipline many single-page apps skip: every URL is a real, loadable page.
How React differs
React builds a tree of components in JavaScript. Each component holds state, and when state changes React recomputes the affected part of the tree and updates the browser's DOM (the in-memory structure of the page). The server typically exposes data, often as JSON, and the client decides how to display it.
Here is the same search-as-you-type feature in React:
function SearchBar() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
if (!query) return;
const timer = setTimeout(async () => {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
setResults(await response.json());
}, 300);
return () => clearTimeout(timer);
}, [query]);
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>
{results.map((r) => <li key={r.id}>{r.name}</li>)}
</ul>
</>
);
}
It is longer, but you now hold complete control over client state. You can show optimistic results, cache them, cancel stale requests, or combine this input with five other widgets that read the same data. That control is the product React sells.
React has moved toward the server too
The picture of React as purely client-side is out of date. React 19 became stable on December 5, 2024, and its release post lists Actions for form handling and stable React Server Components as headline features (React 19 announcement). The React documentation describes Server Components as components that render "ahead of time, before bundling, in an environment separate from your client app or SSR server," and its own example shows a Markdown-rendering component whose libraries would otherwise cost users "an additional 75K (gzipped)" in the browser (React Server Components reference).
So both camps are converging on one idea: do more work where the data lives, and send less code to the browser. The difference is the contract. With React Server Components the server sends a serialized component tree to a React runtime that still owns the page. With htmx the server sends HTML and the browser's native machinery does the rest. Our piece on Next.js vs React covers the framework layer that makes Server Components usable in practice.
What the numbers say, and what they do not
Performance claims in this debate are often invented, so it is worth being precise about what is actually measured. There is no neutral, widely accepted benchmark that proves one approach is faster in general. What exists is a set of narrower facts.
| Fact | Figure | Source |
|---|---|---|
| htmx size, minified and gzipped | about 16 KB | htmx.org |
| Datastar (alternative) single file | 11.90 KiB | data-star.dev |
| Median JavaScript per page, mobile, 2024 | 558 KB (up from 491 KB) | HTTP Archive Web Almanac 2024 |
| Median JavaScript per page, desktop, 2024 | 613 KB (up from 538 KB) | HTTP Archive Web Almanac 2024 |
| Extra library cost avoided by a Server Component in React's own example | 75 KB gzipped | react.dev |
| "Good" Interaction to Next Paint (INP) threshold | 200 ms or less at the 75th percentile | web.dev |
Two caveats apply. First, the Web Almanac figures describe the median web page, not React apps specifically, and the chapter does not state whether the numbers are compressed or decoded sizes. Second, a small library does not guarantee a fast site. Your own application code, images, fonts, database queries, and third-party scripts usually dominate.
The one well-documented port
The best public data point is a case study written by htmx creator Carson Gross on September 29, 2022. It reports a DjangoCon 2022 talk by David Guillot on how the company Contexte ported a SaaS product from React to Django templates and htmx. The reported results (htmx essay):
| Metric | React version | htmx version | Change |
|---|---|---|---|
| Total lines of code | 21,500 | 7,200 | 67% fewer |
| Python lines | 500 | 1,200 | 140% more |
| JavaScript dependencies | 255 | 9 | 96% fewer |
| Web build time | 40 seconds | 5 seconds | 88% less |
| Memory use | 75 MB | 45 MB | 46% less |
| Time to interactive (first load) | 2 to 6 seconds | 1 to 2 seconds | 50 to 60% less |
The port took about two months for a codebase of roughly 21,000 lines, mostly JavaScript. Notice what moved: the Python grew, because logic that used to live in the browser came back to the server. The total shrank because a lot of React code existed only to translate between server data and screen state.
Treat this as one team's result, reported by an advocate of the library. It does not show that every React app will shrink by two thirds. It does show what "less glue code" can look like when the application was mostly forms, lists, and server data.
Where htmx genuinely wins
Form-driven CRUD and admin tools
Create, read, update, delete: this is the shape HTML was designed for. An inline edit pattern needs one request to fetch the form and one to save it.
<tr id="user-123">
<td>Alice</td>
<td>
<button hx-get="/users/123/edit" hx-target="#user-123" hx-swap="outerHTML">
Edit
</button>
</td>
</tr>
The server responds to the GET with a <tr> containing inputs, and to the subsequent PUT with the updated static <tr>. The row replaces itself both times. There is no store to keep in sync with the server, because the server is the only source of truth.
Server-heavy logic
If permissions, pricing rules, or validation already live in Python, Ruby, or Go, htmx avoids duplicating them in the browser. You render the allowed buttons directly and skip shipping a rules engine to the client. When you are choosing a backend for that job, our comparison of FastAPI, Flask, and Django covers the main Python options.
Content that needs to be found
Server-rendered HTML is present in the first response, so crawlers and slow devices both get content without waiting for a script to run. That helps when search visibility matters, and it connects to a broader point covered in why web performance affects SEO. React can reach the same outcome through server rendering; the difference is that with htmx it is the default rather than something you configure. For background on that general technique, see what server-side rendering solves.
Small teams and long-lived apps
When one person writes both the endpoint and the template in one language, a feature is one pull request. Carson Gross argues in his scaling essay that hypermedia applications "scale into the 10s" of developers and that "10 developers should be enough for any application," while saying he cannot answer for teams of 100 or more (Does Hypermedia Scale?). That is a claim from a library author, not an industry study, but it frames the honest boundary: the model is proven for small and mid-sized teams and untested at very large ones.
Accessibility by inheritance
Plain links, forms, and buttons come with keyboard behavior, focus handling, and screen reader semantics built in. A hypermedia app starts from that baseline and adds behavior on top. A client-rendered app has to rebuild some of it, which is one reason accessibility pays off for web products in either approach. htmx does not make a site accessible automatically; swapped content still needs sensible focus management and announcements.
Where React genuinely wins
Dense client-side interaction
The htmx authors are direct about the limits. In their guidance, spreadsheet-like applications where users create arbitrary formulas are a poor fit, because the server cannot know the dependencies at render time. Apps that respond to every mouse movement, such as a map, are also a poor fit, since a server round trip is too slow (When To Use Hypermedia?). Drag-and-drop canvases, design tools, and collaborative editors live in this territory.
Offline-first software
The same essay says that if an application "requires full functionality in an offline environment," hypermedia is not acceptable. Service Workers can help but add complexity. If your users work on planes, in basements, or in the field with poor signal, state has to live on the device, and a client framework is the natural home for it.
Complex shared client state
A shopping cart that updates from several places, undo and redo, optimistic updates, and coordinated panels are all easier when one in-browser store is the source of truth. You can imitate this with htmx using out-of-band swaps and response headers like HX-Trigger, but at some point you are rebuilding a state container out of server responses.
Component ecosystems and hiring
The htmx guidance acknowledges that copy-and-paste component libraries such as shadcn/ui are built for React and do not integrate well with htmx. If your design system already lives in those components, the integration cost is real; see our overview of what shadcn/ui is for how that model works. Team fit counts too. A group of ten engineers fluent in React will ship faster in React than in a library they must learn, and the htmx essay itself suggests trying hypermedia first on internal tools rather than betting a flagship product on it.
A worked example: an internal user-admin screen
Consider a page listing users with inline editing. This is the kind of work where the two approaches diverge most clearly.
The React-style version needs a data endpoint (for example /api/users returning JSON), a list component, a row component, an edit form component, local state for the form fields, error and loading states, and a way to refresh the list after a save. Each piece is small, but there are many pieces, and the contract between JSON shape and component props has to be maintained. If you are weighing API styles for that contract, GraphQL vs REST vs tRPC covers the tradeoffs.
The htmx version needs three routes and two templates:
# Flask example
@app.route('/admin/users')
def list_users():
return render_template('users.html', users=db.users.all())
@app.route('/admin/users/<int:id>/edit')
def edit_user_form(id):
return render_template('partials/edit_row.html', user=db.users.get(id))
@app.route('/admin/users/<int:id>', methods=['PUT'])
def update_user(id):
db.users.update(id, request.form)
return render_template('partials/user_row.html', user=db.users.get(id))
The row template contains one button with hx-get, and the edit template contains one form with hx-put. If JavaScript fails to load, you can still build the same page with ordinary links and forms. Whether this is a 5x or a 2x reduction in code depends on the team and the codebase, which is why we avoid inventing a ratio here. The Contexte port above is the nearest real measurement.
Detecting htmx requests
Because endpoints return fragments, they should also work as full pages when someone opens the URL directly. htmx sends an HX-Request: true header on its requests, so a server can return a partial for htmx and a full layout for everyone else (htmx docs). Use that header, and avoid maintaining two separate sets of URLs.
Real-time features, WebSockets, and polling
htmx handles live updates through three mechanisms, and each has a distinct cost profile.
- Polling with
hx-trigger="every 10s"is the simplest. It can be limited to visible tabs by adding a filter such as[!document.hidden](hx-trigger reference). - Server-Sent Events (a one-way stream from server to browser) come through the
sseextension. - WebSockets (a two-way connection) come through the
wsextension.
In htmx 2.x the WebSocket extension is enabled with hx-ext="ws" and connected with ws-connect. Elements marked ws-send post their values to the socket as JSON, and incoming messages are parsed as HTML and swapped in by matching id, in the same way as out-of-band swaps (WebSockets extension).
<div hx-ext="ws" ws-connect="/chatroom">
<div id="notifications"></div>
<form id="form" ws-send>
<input name="chat_message">
</form>
</div>
Older tutorials show an hx-ws attribute from htmx 1. Check the documentation for the version you install, because the syntax changed and the extensions are no longer part of the core file.
Alternatives in the same family
htmx is one of several tools that send HTML or HTML-like updates from the server.
- Hotwire (Turbo and Stimulus). Its site describes it as a way to build modern web apps "without using much JavaScript" by sending HTML instead of JSON. Turbo speeds up page changes and form submissions and can stream partial updates; Stimulus handles the occasional custom behavior (hotwired.dev).
- Phoenix LiveView. Each page is first rendered as a regular HTTP response, then the browser opens a WebSocket to a server process that holds the state. After each event, LiveView computes diffs and sends only the changed parts (Phoenix LiveView docs). It requires Elixir.
- Datastar. It describes itself as a lightweight framework that drives the frontend with HTML attributes and moves state management to the backend, in a single 11.90 KiB file, and it accepts both HTML and event-stream responses (data-star.dev).
- React Server Components. Covered above: a React-native route to server-side rendering with the client runtime still in charge.
The htmx project treats this as a category. Its essay on hypermedia-driven applications names htmx, Unpoly, Hotwire, and Hyperview as examples and describes the architecture as combining "the simplicity & flexibility of traditional Multi-Page Applications (MPAs)" with a better user experience (Hypermedia-Driven Applications).
Security and operational costs of the htmx model
Sending HTML from the server shifts some risks rather than removing them.
Escaping. Anything you inject into a fragment must be escaped. The htmx docs say to escape all user content to prevent cross-site scripting (XSS), and, for third-party content you cannot escape, to strip hx- and data-hx attributes and inline scripts. The hx-disable attribute tells htmx to ignore an element and its children, and injected content cannot re-enable processing inside that subtree. The docs also recommend a Content Security Policy such as default-src 'self' as a browser-level layer (htmx docs).
Response headers on redirects. htmx does not process response headers on 3xx redirects, so if you rely on headers like HX-Redirect or HX-Trigger, return a 200-range response (htmx docs).
Latency on every interaction. An htmx interaction is a network round trip. On a good connection that is fast, and the scaling essay claims endpoints can reach "sub-100 millisecond response times" with query tuning, but on a congested mobile network every click waits. A client framework can show an optimistic result immediately. This ties back to the INP metric in the table above: INP rewards pages that respond quickly to input, and where you spend your latency budget is an architecture choice. For measurement tips, see our guide to web performance optimization.
Server load. Rendering HTML for each interaction uses server CPU that a client app would have spent on the user's device. For internal tools with hundreds of users this is negligible. For a consumer product with millions of daily sessions, capacity planning matters, and caching strategy changes because fragments are often user-specific.
Five misconceptions worth retiring
"htmx is only for simple apps"
The better description is that it suits apps whose complexity is mostly on the server. The htmx authors say server-side complex features suit the model well and client-side complex ones do not. Complexity alone is not the test; its location is.
"React is always more powerful"
React gives you more control over the client. That is not the same as being better at every job. For form-heavy CRUD, the port above suggests a large share of React code can be glue between data and display.
"htmx means no JavaScript"
The htmx essay on hypermedia-driven applications says scripting is optional and should enhance the front end without replacing hypermedia, naming tools such as hyperscript, Alpine.js, and plain JavaScript. You will still write some JavaScript. You write less of it, and it stays local.
"Server rendering is slow"
Rendering HTML is rarely the slow step. Database queries, network latency, and oversized client bundles usually matter more. A well-cached server render can beat a client render that must download, parse, and run a bundle before painting. The browser-side mechanics are covered in how browsers render web pages.
"You have to pick one forever"
Both tools are designed to be added incrementally. You can sprinkle htmx onto an existing server-rendered app for a few features, or mount a React widget inside a server-rendered page for a complex chart. The architectures are not mutually exclusive.
When to pick what
| If you are building | Reasonable default | Notes |
|---|---|---|
| Internal admin tool | htmx | Fewer moving parts; try it first |
| B2B SaaS, mostly forms and tables | htmx or React | Decide by team skills and later mobile plans |
| Collaboration tool, design tool, spreadsheet | React | Client state is the product |
| Marketing site | Static HTML or a static-site generator | Neither is needed |
| Public site that needs rich widgets | Server rendering plus islands | Keep most pages as HTML |
| App that must work offline | React or another client framework | State lives on the device |
| Realtime dashboards | Either, with SSE or WebSockets | LiveView is another option if you use Elixir |
If you are still deciding what to learn, the complete web development learning path puts the fundamentals in order, and our React vs Vue vs Svelte comparison covers the client-side side of the decision.
FAQ
Q: Will htmx replace React? A: No evidence points that way. React 19 became stable in December 2024 and continues to add server-side features, while htmx targets a different architecture. They overlap on forms and lists and diverge on rich client-side interaction.
Q: Is htmx easier to learn than React? A: The first hour is easier, because the attribute-based API needs no build step. Production use takes longer, because you must design fragment endpoints, handle history and redirects, and think about escaping. Your server-side skills matter more than they would with React.
Q: Can I adopt htmx gradually?
A: Yes. You can add the script to an existing server-rendered application and enhance one form or one table at a time. The hx-boost attribute upgrades ordinary links and forms while leaving them functional without JavaScript.
Q: How does htmx affect SEO? A: The first response is server-rendered HTML, so content is present without client-side rendering. That removes one common risk of single-page apps, though you still need sensible titles, links, and URLs. Any URL pushed into history must return a full page when loaded directly.
Q: Can htmx power a mobile app? A: Not by itself. htmx is a browser library. You can serve a responsive web app or a progressive web app, but native features and offline behavior need other tools. The htmx authors list offline-first apps among the cases where hypermedia is a poor fit.
Q: Is the 67 percent code reduction typical? A: It is one reported case, not a benchmark. The Contexte port cut lines from 21,500 to 7,200 on a product that was mostly forms and server data. Results will vary with how much of your React code is glue and how much is real client-side logic.
The takeaway
The choice between htmx and React is a choice about where state lives. Put it on the server and you get less code, native browser behavior, and a model that fits CRUD tools and content sites. Put it in the browser and you get immediate feedback, offline capability, and a deep component ecosystem, at the price of more machinery. The most credible data point available, a single documented port, shows the savings can be large when an app is mostly forms and lists, and the library authors themselves name spreadsheets, maps, and offline software as cases where you should not use their tool. Start from your application's hardest problem, not from the framework everyone else picked.
Sources
- htmx home page and documentation
- A Real World React -> htmx Port, Carson Gross, September 29, 2022
- When To Use Hypermedia?, Does Hypermedia Scale?, and Hypermedia-Driven Applications
- React 19 release post and React Server Components reference
- HTTP Archive Web Almanac 2024: JavaScript and web.dev on INP
- Hotwire, Phoenix LiveView, and Datastar
Follow new articles
Add SciHub101 to your feed reader to see new science and technology stories.
About the Author
SciHub101 is an independent science and technology publication founded and edited by Buğra Sözeri, a software engineer and app developer based in Türkiye. AI tools assist with drafting and research. Source documentation in the existing archive is uneven, and articles are being reviewed and corrected. The Editorial Team byline identifies editorial responsibility; it does not certify specialist or independent human review. Where we compare products, anything built by our own team is labelled as such — see the editorial guidelines and About page for how corrections and disclosures work.
Related Articles

GraphQL vs REST vs tRPC 2026: API Architecture Pick
GraphQL, REST and tRPC compared on caching, type safety, security, versioning and tooling, with a decision guide for public, mobile and TypeScript APIs.

Web App Monitoring Tools 2026 — Sentry & Datadog Picks
Web app monitoring tools 2026: Sentry vs Datadog vs Better Stack. Error tracking, performance monitoring, uptime, logs. Build a production monitoring stack.

What Is HTMX? Hypermedia for Modern Web
HTMX explained: how this 14KB library lets you build dynamic web apps with HTML attributes instead of a JavaScript framework, plus how it actually works.

Next.js vs React 2026 — Which Should You Learn First?
Next.js is a framework built on React, not a rival. What each does, Server Components, App vs Pages Router, and when to pick which, per the official docs.

React vs Vue vs Svelte 2026 — Which Framework to Learn?
Compare React, Vue, Svelte side-by-side. Job market demand, learning curve, salary, real-world usage. Which one to choose.

CSS Flexbox and Grid — Complete Layout Guide
Master CSS Flexbox and Grid with practical code examples. Learn when to use each, key properties, and common layout patterns every developer needs.