{"schemaVersion":1,"id":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications","title":"HTMX 2.0: Hypermedia-Driven Applications in Production","description":"A technical analysis of HTMX 2.0's hypermedia-driven approach, hx-boost, WebSocket extensions, and server-sent events. Includes benchmarks against React and an assessment of when the approach is appropriate.","language":"en","datePublished":"2025-11-14","dateModified":null,"contentFormat":"text/markdown","content":"Frontend\n\n# HTMX 2.0: Hypermedia-Driven Applications in Production\n\n2025-11-147 min read\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nAlso available in: [Español](<https://lucioduran.com/es/blog/htmx-2-hypermedia-driven-applications>)\n\n## The Case Against Single-Page Applications\n\nThe following numbers provide context. I audited 12 production React SPAs from clients over the past year. Average JavaScript bundle: 387KB gzipped. Average time-to-interactive on a mid-range Android device: 4.2 seconds. Average number of npm dependencies: 1,847.\n\nEvery single one of those apps was fundamentally doing the same thing: fetching data from an API, rendering it as HTML, and letting users submit forms. We'd invented this incredibly sophisticated pipeline, JSX transpilation, virtual DOM diffing, state management, hydration, to do what the browser already does natively with ` <a> ` tags and ` <form> ` elements.\n\n[HTMX](<https://htmx.org/>)'s thesis is simple and almost offensively obvious: **what if we just sent HTML over the wire?**\n\n## HTMX 2.0: What Actually Changed\n\nHTMX 2.0 (released stable in late 2025) isn't a revolution over 1.x, but the refinements matter. Here's what's actually different:\n\n### The ` hx-on ` Syntax Overhaul\n\nThis one bit me during the migration. The old syntax:\n\n```html\n<button hx-post=\"/api/items\"\n hx-on::after-request=\"handleResponse(event)\">\n Save\n</button>\n```\n\nBecame:\n\n```html\n<button hx-post=\"/api/items\"\n hx-on:htmx:after-request=\"handleResponse(event)\">\n Save\n</button>\n```\n\nThe double-colon was causing issues with certain HTML parsers and CSP policies. The new syntax is more explicit and, importantly, plays nicer with Content Security Policy headers that disallow inline event handlers, since HTMX registers them programmatically.\n\n### Native WebSocket Extension (Stable)\n\nThe WebSocket extension graduated from experimental in 2.0, and it's genuinely excellent:\n\n```html\n<div hx-ext=\"ws\" ws-connect=\"/ws/notifications\">\n <div id=\"notification-feed\" hx-swap-oob=\"beforeend\">\n <!-- Server pushes HTML fragments here -->\n </div>\n</div>\n```\n\nOn the server side (Go example):\n\n```go\nfunc handleWS(w http.ResponseWriter, r *http.Request) {\n conn, err := upgrader.Upgrade(w, r, nil)\n if err != nil {\n return\n }\n defer conn.Close()\n\n for notification := range userNotifications(r.Context()) {\n html := renderNotificationHTML(notification)\n conn.WriteMessage(websocket.TextMessage, []byte(html))\n }\n}\n```\n\nNo JSON serialization. No client-side template rendering. No state synchronization. The server sends a ` <div> ` with the notification content, and HTMX drops it into the DOM. That's it.\n\n### Server-Sent Events (The Underrated Gem)\n\nFor unidirectional real-time data, SSE is almost always the right choice over WebSockets. HTMX 2.0 makes it trivial:\n\n```html\n<div hx-ext=\"sse\" sse-connect=\"/events/dashboard-metrics\">\n <div sse-swap=\"cpu-update\" hx-swap=\"innerHTML settle:300ms\">\n <span class=\"metric\">Loading...</span>\n </div>\n <div sse-swap=\"memory-update\" hx-swap=\"innerHTML\">\n <span class=\"metric\">Loading...</span>\n </div>\n <div sse-swap=\"request-count\" hx-swap=\"innerHTML\">\n <span class=\"metric\">Loading...</span>\n </div>\n</div>\n```\n\nEach SSE event type maps to a different swap target. The server sends named events with HTML payloads, and each ` sse-swap ` attribute catches the event it cares about. I used this pattern for the entire monitoring dashboard, real-time charts and all. The charts themselves are server-rendered SVGs that get swapped in. No Chart.js, no D3 on the client.\n\n## hx-boost: Progressive Enhancement Done Right\n\n` hx-boost ` is probably the single feature that sells HTMX to skeptics. Add it to your body tag:\n\n```html\n<body hx-boost=\"true\">\n```\n\nAnd every ` <a> ` tag and ` <form> ` in your page is automatically \"boosted\", clicks and submissions are intercepted, the request is made via AJAX, and only the ` <body> ` content is swapped. The browser URL updates, history works, back/forward works. Your multi-page app feels like a SPA with zero code changes.\n\nA significant advantage: if JavaScript fails to load (or is disabled), everything still works as normal links and form submissions. That's not a theoretical benefit, I've had clients in regions with flaky mobile connections where JS bundles fail to load 8-12% of the time. Those users still get a fully functional app.\n\n## Real Comparison: React vs HTMX for a CRUD Dashboard\n\nI rebuilt the same feature, a paginated, filterable user management table with inline editing, in both React (Next.js 15) and HTMX (Go + templ). Here are the actual numbers:\n\n| Metric | React (Next.js) | HTMX + Go |\n| --- | --- | --- |\n| JS sent to client | 127KB gzipped | 14KB (htmx.min.js) |\n| Time to Interactive (3G) | 3.1s | 0.8s |\n| Server response time (p95) | 45ms (API JSON) | 38ms (HTML fragment) |\n| Lines of client code | 847 (JSX + hooks) | 12 (vanilla JS for confirm dialogs) |\n| Lines of server code | 203 (API routes) | 341 (templates + handlers) |\n| Build step | 34s (Next.js build) | 1.2s (Go compile) |\n| Dev dependencies | 387 packages | 12 packages |\n\nThe tradeoff is clear: you write more server-side code with HTMX, but dramatically less client-side code. Total code? About the same. But the HTMX version has almost no moving parts on the client, which means fewer bugs in the part of the stack that's hardest to debug.\n\n## The Pattern That Changed Everything: OOB Swaps\n\nOut-of-band swaps are HTMX's answer to \"but what about updating multiple parts of the page?\" When the server responds to a request, it can include extra HTML fragments tagged with ` hx-swap-oob `:\n\n```html\n<!-- Primary response (swaps into the target) -->\n<tr id=\"user-42\">\n <td>Updated Name</td>\n <td>updated@email.com</td>\n</tr>\n\n<!-- Out-of-band: also update the notification count -->\n<span id=\"notification-count\" hx-swap-oob=\"true\">3</span>\n\n<!-- Out-of-band: also update the last-modified timestamp -->\n<time id=\"last-modified\" hx-swap-oob=\"true\">Just now</time>\n```\n\nOne HTTP response updates three different parts of the page. No client-side state management. No Redux. No Zustand. No signals. The server knows the full state, renders all the HTML fragments that changed, and sends them in a single response.\n\nI was skeptical about this pattern until I used it in production. It's absurdly effective for the 90% case.\n\n## When HTMX Is the Wrong Choice\n\nI'd be dishonest if I didn't cover the cases where I'd still reach for React:\n\n**Collaborative real-time editing**, think Google Docs or Figma. The conflict resolution and operational transforms need client-side logic that doesn't map to hypermedia.\n\n**Offline-first applications**, HTMX needs a server. If your app must work without network, you need a client-side data layer.\n\n**Complex drag-and-drop interfaces**, Kanban boards, workflow builders. The interaction model is fundamentally client-side.\n\n**Dense data visualizations with interactivity**, Zooming, panning, brushing on charts. Server-rendered SVGs work for display, but interactive exploration needs client JS.\n\nFor everything else, and I mean this seriously, HTMX is not just viable, it's often the better choice.\n\n## Migration Strategy: The Strangler Fig\n\nYou don't have to rewrite. I migrated the client dashboard incrementally:\n\n1. **Week 1**: Added ` hx-boost ` to the layout. Instant SPA-like navigation, no other changes.\n2. **Week 2-3**: Converted the simplest CRUD pages to server-rendered HTML with HTMX attributes. Removed their React components.\n3. **Week 4-5**: Tackled the complex forms. Used ` hx-target ` and ` hx-swap ` for inline validation and partial updates.\n4. **Week 6**: Converted the real-time notification system from a React + WebSocket client to HTMX's SSE extension.\n5. **Week 7**: Removed React, ReactDOM, Next.js, and 374 npm packages from the project.\n\nThe app was in production the entire time. Users didn't notice the switch.\n\n## The Preload Extension: Perceived Performance\n\nOne more pattern worth sharing. HTMX's preload extension makes navigation feel instant:\n\n```html\n<body hx-boost=\"true\" hx-ext=\"preload\">\n <nav>\n <a href=\"/dashboard\" preload=\"mousedown\">Dashboard</a>\n <a href=\"/users\" preload=\"mousedown\">Users</a>\n <a href=\"/settings\" preload=\"mousedown\">Settings</a>\n </nav>\n</body>\n```\n\n` preload=\"mousedown\" ` fires the request on mousedown (which happens ~100-150ms before the click event). By the time the click registers and HTMX processes the boost, the response is usually already cached. Combined with server response times under 50ms, navigation feels literally instant.\n\n## Final Thoughts\n\nHTMX 2.0 isn't going to replace React for every project. But it's forced me to confront an uncomfortable question: how much of the complexity in my frontend stack exists to solve problems that the browser already solved in 1999?\n\nThe answer, for most of the apps I build, is \"most of it.\"\n\nThe web was designed as a hypermedia system. HTMX just leans into that design instead of fighting it. And after six months of building with it in production, I can tell you: it's not a toy. It's not a step backward. It's a genuinely different (and often better) way to build web applications.\n\nA weekend project is sufficient to evaluate the approach for a given use case.\n\n## Frequently asked questions\n\n### Is HTMX suitable for complex interactive applications?\n\nIt depends on the interaction pattern. HTMX excels at CRUD-heavy apps, dashboards, and content sites. For highly interactive UIs like collaborative editors or complex drag-and-drop, you'll still want a client-side framework, or a hybrid approach.\n\n### Can HTMX work alongside React or Vue components?\n\nAbsolutely. HTMX 2.0 plays well with islands architecture. You can use HTMX for page-level navigation and data fetching while mounting React/Vue for specific interactive widgets.\n\n### How does HTMX handle SEO compared to SPAs?\n\nSince HTMX serves real HTML from the server, SEO works out of the box, no SSR hydration, no prerendering hacks. Search engines see the same content users do.\n\n## Tips\n\n1. **Pro tip:** Use ` hx-boost='true' ` on your ` <body> ` tag to progressively enhance every anchor and form, zero JavaScript required for navigation.\n2. **Watch out:** HTMX 2.0's ` hx-on ` syntax changed from ` hx-on::after-request ` to ` hx-on:htmx:after-request `. Old tutorials will burn you.\n3. **Performance trick:** Combine ` hx-push-url='true' ` with ` preload ` extension to get perceived instant navigation that rivals any SPA router.\n4. **SSE secret:** The ` sse-swap ` attribute with ` hx-swap='beforeend settle:500ms' ` gives you smooth real-time updates without a single line of client JS.\n\n- htmx\n- hypermedia\n- frontend\n- react\n- ssr\n- websockets\n- sse\n- spa-fatigue\n\n[Back to Blog](<https://lucioduran.com/blog>)","headings":[{"level":1,"text":"HTMX 2.0: Hypermedia-Driven Applications in Production","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#title"},{"level":2,"text":"The Case Against Single-Page Applications","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-case-against-single-page-applications"},{"level":2,"text":"HTMX 2.0: What Actually Changed","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#htmx-20-what-actually-changed"},{"level":3,"text":"The hx-on Syntax Overhaul","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-hx-on-syntax-overhaul"},{"level":3,"text":"Native WebSocket Extension (Stable)","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#native-websocket-extension-stable"},{"level":3,"text":"Server-Sent Events (The Underrated Gem)","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#server-sent-events-the-underrated-gem"},{"level":2,"text":"hx-boost: Progressive Enhancement Done Right","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#hx-boost-progressive-enhancement-done-right"},{"level":2,"text":"Real Comparison: React vs HTMX for a CRUD Dashboard","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#real-comparison-react-vs-htmx-for-a-crud-dashboard"},{"level":2,"text":"The Pattern That Changed Everything: OOB Swaps","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-pattern-that-changed-everything-oob-swaps"},{"level":2,"text":"When HTMX Is the Wrong Choice","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#when-htmx-is-the-wrong-choice"},{"level":2,"text":"Migration Strategy: The Strangler Fig","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#migration-strategy-the-strangler-fig"},{"level":2,"text":"The Preload Extension: Perceived Performance","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-preload-extension-perceived-performance"},{"level":2,"text":"Final Thoughts","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#final-thoughts"},{"level":2,"text":"Frequently asked questions","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#questions"},{"level":3,"text":"Is HTMX suitable for complex interactive applications?","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#question-1"},{"level":3,"text":"Can HTMX work alongside React or Vue components?","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#question-2"},{"level":3,"text":"How does HTMX handle SEO compared to SPAs?","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#article-tips"}],"sections":[{"heading":"HTMX 2.0: Hypermedia-Driven Applications in Production","level":1,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications","content":"Frontend"},{"heading":"HTMX 2.0: Hypermedia-Driven Applications in Production","level":1,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#title","content":"2025-11-147 min read\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nAlso available in: [Español](<https://lucioduran.com/es/blog/htmx-2-hypermedia-driven-applications>)"},{"heading":"The Case Against Single-Page Applications","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-case-against-single-page-applications","content":"The following numbers provide context. I audited 12 production React SPAs from clients over the past year. Average JavaScript bundle: 387KB gzipped. Average time-to-interactive on a mid-range Android device: 4.2 seconds. Average number of npm dependencies: 1,847.\n\nEvery single one of those apps was fundamentally doing the same thing: fetching data from an API, rendering it as HTML, and letting users submit forms. We'd invented this incredibly sophisticated pipeline, JSX transpilation, virtual DOM diffing, state management, hydration, to do what the browser already does natively with ` <a> ` tags and ` <form> ` elements.\n\n[HTMX](<https://htmx.org/>)'s thesis is simple and almost offensively obvious: **what if we just sent HTML over the wire?**"},{"heading":"HTMX 2.0: What Actually Changed","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#htmx-20-what-actually-changed","content":"HTMX 2.0 (released stable in late 2025) isn't a revolution over 1.x, but the refinements matter. Here's what's actually different:"},{"heading":"The hx-on Syntax Overhaul","level":3,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-hx-on-syntax-overhaul","content":"This one bit me during the migration. The old syntax:\n\n```html\n<button hx-post=\"/api/items\"\n hx-on::after-request=\"handleResponse(event)\">\n Save\n</button>\n```\n\nBecame:\n\n```html\n<button hx-post=\"/api/items\"\n hx-on:htmx:after-request=\"handleResponse(event)\">\n Save\n</button>\n```\n\nThe double-colon was causing issues with certain HTML parsers and CSP policies. The new syntax is more explicit and, importantly, plays nicer with Content Security Policy headers that disallow inline event handlers, since HTMX registers them programmatically."},{"heading":"Native WebSocket Extension (Stable)","level":3,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#native-websocket-extension-stable","content":"The WebSocket extension graduated from experimental in 2.0, and it's genuinely excellent:\n\n```html\n<div hx-ext=\"ws\" ws-connect=\"/ws/notifications\">\n <div id=\"notification-feed\" hx-swap-oob=\"beforeend\">\n <!-- Server pushes HTML fragments here -->\n </div>\n</div>\n```\n\nOn the server side (Go example):\n\n```go\nfunc handleWS(w http.ResponseWriter, r *http.Request) {\n conn, err := upgrader.Upgrade(w, r, nil)\n if err != nil {\n return\n }\n defer conn.Close()\n\n for notification := range userNotifications(r.Context()) {\n html := renderNotificationHTML(notification)\n conn.WriteMessage(websocket.TextMessage, []byte(html))\n }\n}\n```\n\nNo JSON serialization. No client-side template rendering. No state synchronization. The server sends a ` <div> ` with the notification content, and HTMX drops it into the DOM. That's it."},{"heading":"Server-Sent Events (The Underrated Gem)","level":3,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#server-sent-events-the-underrated-gem","content":"For unidirectional real-time data, SSE is almost always the right choice over WebSockets. HTMX 2.0 makes it trivial:\n\n```html\n<div hx-ext=\"sse\" sse-connect=\"/events/dashboard-metrics\">\n <div sse-swap=\"cpu-update\" hx-swap=\"innerHTML settle:300ms\">\n <span class=\"metric\">Loading...</span>\n </div>\n <div sse-swap=\"memory-update\" hx-swap=\"innerHTML\">\n <span class=\"metric\">Loading...</span>\n </div>\n <div sse-swap=\"request-count\" hx-swap=\"innerHTML\">\n <span class=\"metric\">Loading...</span>\n </div>\n</div>\n```\n\nEach SSE event type maps to a different swap target. The server sends named events with HTML payloads, and each ` sse-swap ` attribute catches the event it cares about. I used this pattern for the entire monitoring dashboard, real-time charts and all. The charts themselves are server-rendered SVGs that get swapped in. No Chart.js, no D3 on the client."},{"heading":"hx-boost: Progressive Enhancement Done Right","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#hx-boost-progressive-enhancement-done-right","content":"` hx-boost ` is probably the single feature that sells HTMX to skeptics. Add it to your body tag:\n\n```html\n<body hx-boost=\"true\">\n```\n\nAnd every ` <a> ` tag and ` <form> ` in your page is automatically \"boosted\", clicks and submissions are intercepted, the request is made via AJAX, and only the ` <body> ` content is swapped. The browser URL updates, history works, back/forward works. Your multi-page app feels like a SPA with zero code changes.\n\nA significant advantage: if JavaScript fails to load (or is disabled), everything still works as normal links and form submissions. That's not a theoretical benefit, I've had clients in regions with flaky mobile connections where JS bundles fail to load 8-12% of the time. Those users still get a fully functional app."},{"heading":"Real Comparison: React vs HTMX for a CRUD Dashboard","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#real-comparison-react-vs-htmx-for-a-crud-dashboard","content":"I rebuilt the same feature, a paginated, filterable user management table with inline editing, in both React (Next.js 15) and HTMX (Go + templ). Here are the actual numbers:\n\n| Metric | React (Next.js) | HTMX + Go |\n| --- | --- | --- |\n| JS sent to client | 127KB gzipped | 14KB (htmx.min.js) |\n| Time to Interactive (3G) | 3.1s | 0.8s |\n| Server response time (p95) | 45ms (API JSON) | 38ms (HTML fragment) |\n| Lines of client code | 847 (JSX + hooks) | 12 (vanilla JS for confirm dialogs) |\n| Lines of server code | 203 (API routes) | 341 (templates + handlers) |\n| Build step | 34s (Next.js build) | 1.2s (Go compile) |\n| Dev dependencies | 387 packages | 12 packages |\n\nThe tradeoff is clear: you write more server-side code with HTMX, but dramatically less client-side code. Total code? About the same. But the HTMX version has almost no moving parts on the client, which means fewer bugs in the part of the stack that's hardest to debug."},{"heading":"The Pattern That Changed Everything: OOB Swaps","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-pattern-that-changed-everything-oob-swaps","content":"Out-of-band swaps are HTMX's answer to \"but what about updating multiple parts of the page?\" When the server responds to a request, it can include extra HTML fragments tagged with ` hx-swap-oob `:\n\n```html\n<!-- Primary response (swaps into the target) -->\n<tr id=\"user-42\">\n <td>Updated Name</td>\n <td>updated@email.com</td>\n</tr>\n\n<!-- Out-of-band: also update the notification count -->\n<span id=\"notification-count\" hx-swap-oob=\"true\">3</span>\n\n<!-- Out-of-band: also update the last-modified timestamp -->\n<time id=\"last-modified\" hx-swap-oob=\"true\">Just now</time>\n```\n\nOne HTTP response updates three different parts of the page. No client-side state management. No Redux. No Zustand. No signals. The server knows the full state, renders all the HTML fragments that changed, and sends them in a single response.\n\nI was skeptical about this pattern until I used it in production. It's absurdly effective for the 90% case."},{"heading":"When HTMX Is the Wrong Choice","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#when-htmx-is-the-wrong-choice","content":"I'd be dishonest if I didn't cover the cases where I'd still reach for React:\n\n**Collaborative real-time editing**, think Google Docs or Figma. The conflict resolution and operational transforms need client-side logic that doesn't map to hypermedia.\n\n**Offline-first applications**, HTMX needs a server. If your app must work without network, you need a client-side data layer.\n\n**Complex drag-and-drop interfaces**, Kanban boards, workflow builders. The interaction model is fundamentally client-side.\n\n**Dense data visualizations with interactivity**, Zooming, panning, brushing on charts. Server-rendered SVGs work for display, but interactive exploration needs client JS.\n\nFor everything else, and I mean this seriously, HTMX is not just viable, it's often the better choice."},{"heading":"Migration Strategy: The Strangler Fig","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#migration-strategy-the-strangler-fig","content":"You don't have to rewrite. I migrated the client dashboard incrementally:\n\n1. **Week 1**: Added ` hx-boost ` to the layout. Instant SPA-like navigation, no other changes.\n2. **Week 2-3**: Converted the simplest CRUD pages to server-rendered HTML with HTMX attributes. Removed their React components.\n3. **Week 4-5**: Tackled the complex forms. Used ` hx-target ` and ` hx-swap ` for inline validation and partial updates.\n4. **Week 6**: Converted the real-time notification system from a React + WebSocket client to HTMX's SSE extension.\n5. **Week 7**: Removed React, ReactDOM, Next.js, and 374 npm packages from the project.\n\nThe app was in production the entire time. Users didn't notice the switch."},{"heading":"The Preload Extension: Perceived Performance","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#the-preload-extension-perceived-performance","content":"One more pattern worth sharing. HTMX's preload extension makes navigation feel instant:\n\n```html\n<body hx-boost=\"true\" hx-ext=\"preload\">\n <nav>\n <a href=\"/dashboard\" preload=\"mousedown\">Dashboard</a>\n <a href=\"/users\" preload=\"mousedown\">Users</a>\n <a href=\"/settings\" preload=\"mousedown\">Settings</a>\n </nav>\n</body>\n```\n\n` preload=\"mousedown\" ` fires the request on mousedown (which happens ~100-150ms before the click event). By the time the click registers and HTMX processes the boost, the response is usually already cached. Combined with server response times under 50ms, navigation feels literally instant."},{"heading":"Final Thoughts","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#final-thoughts","content":"HTMX 2.0 isn't going to replace React for every project. But it's forced me to confront an uncomfortable question: how much of the complexity in my frontend stack exists to solve problems that the browser already solved in 1999?\n\nThe answer, for most of the apps I build, is \"most of it.\"\n\nThe web was designed as a hypermedia system. HTMX just leans into that design instead of fighting it. And after six months of building with it in production, I can tell you: it's not a toy. It's not a step backward. It's a genuinely different (and often better) way to build web applications.\n\nA weekend project is sufficient to evaluate the approach for a given use case."},{"heading":"Frequently asked questions","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#questions","content":""},{"heading":"Is HTMX suitable for complex interactive applications?","level":3,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#question-1","content":"It depends on the interaction pattern. HTMX excels at CRUD-heavy apps, dashboards, and content sites. For highly interactive UIs like collaborative editors or complex drag-and-drop, you'll still want a client-side framework, or a hybrid approach."},{"heading":"Can HTMX work alongside React or Vue components?","level":3,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#question-2","content":"Absolutely. HTMX 2.0 plays well with islands architecture. You can use HTMX for page-level navigation and data fetching while mounting React/Vue for specific interactive widgets."},{"heading":"How does HTMX handle SEO compared to SPAs?","level":3,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#question-3","content":"Since HTMX serves real HTML from the server, SEO works out of the box, no SSR hydration, no prerendering hacks. Search engines see the same content users do."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#article-tips","content":"1. **Pro tip:** Use ` hx-boost='true' ` on your ` <body> ` tag to progressively enhance every anchor and form, zero JavaScript required for navigation.\n2. **Watch out:** HTMX 2.0's ` hx-on ` syntax changed from ` hx-on::after-request ` to ` hx-on:htmx:after-request `. Old tutorials will burn you.\n3. **Performance trick:** Combine ` hx-push-url='true' ` with ` preload ` extension to get perceived instant navigation that rivals any SPA router.\n4. **SSE secret:** The ` sse-swap ` attribute with ` hx-swap='beforeend settle:500ms' ` gives you smooth real-time updates without a single line of client JS.\n\n- htmx\n- hypermedia\n- frontend\n- react\n- ssr\n- websockets\n- sse\n- spa-fatigue\n\n[Back to Blog](<https://lucioduran.com/blog>)"}],"links":[{"text":"Español","url":"https://lucioduran.com/es/blog/htmx-2-hypermedia-driven-applications"},{"text":"HTMX","url":"https://htmx.org/"},{"text":"Back to Blog","url":"https://lucioduran.com/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications"},"headline":"HTMX 2.0: Hypermedia-Driven Applications in Production","description":"A technical analysis of HTMX 2.0's hypermedia-driven approach, hx-boost, WebSocket extensions, and server-sent events. Includes benchmarks against React and an assessment of when the approach is appropriate.","datePublished":"2025-11-14","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://lucioduran.com/favicons/og-default.png","url":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications","inLanguage":"en","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"Frontend","keywords":"htmx, hypermedia, frontend, react, ssr, websockets, sse, spa-fatigue","wordCount":1230,"timeRequired":"PT7M","workTranslation":[{"@type":"BlogPosting","inLanguage":"es","url":"https://lucioduran.com/es/blog/htmx-2-hypermedia-driven-applications"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/blog"},{"@type":"ListItem","position":3,"name":"HTMX 2.0: Hypermedia-Driven Applications in Production","item":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Is HTMX suitable for complex interactive applications?","acceptedAnswer":{"@type":"Answer","text":"It depends on the interaction pattern. HTMX excels at CRUD-heavy apps, dashboards, and content sites. For highly interactive UIs like collaborative editors or complex drag-and-drop, you'll still want a client-side framework, or a hybrid approach."}},{"@type":"Question","name":"Can HTMX work alongside React or Vue components?","acceptedAnswer":{"@type":"Answer","text":"Absolutely. HTMX 2.0 plays well with islands architecture. You can use HTMX for page-level navigation and data fetching while mounting React/Vue for specific interactive widgets."}},{"@type":"Question","name":"How does HTMX handle SEO compared to SPAs?","acceptedAnswer":{"@type":"Answer","text":"Since HTMX serves real HTML from the server, SEO works out of the box, no SSR hydration, no prerendering hacks. Search engines see the same content users do."}}]}]}],"contentHash":"0c8d7c42af66fa75489dcafc9696ff68087f1fb9e57eca612b847fe62de29817","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":62289,"contentBytes":10780}}
