<!-- Canonical: https://lucioduran.com/es/blog/htmx-2-hypermedia-driven-applications
Language: es
Content-SHA256: b5703fa7b62d04cd1f7ea90d910d7971edd41f0c94a4238b29c4eca14d8ff6ac
-->

Frontend

# HTMX 2.0: Aplicaciones Hypermedia en Producción

2025-11-148 min lectura

Lucio Durán
Engineering Manager & AI Solutions Architect

También disponible en: [English](<https://lucioduran.com/blog/htmx-2-hypermedia-driven-applications>)

## El Caso Contra las Single-Page Applications

Vamos a los números. Audité 12 SPAs React en producción de clientes durante el último año. Bundle JavaScript promedio: 387KB gzipped. Tiempo promedio hasta interactivo en un Android de gama media: 4.2 segundos. Número promedio de dependencias npm: 1,847.

Cada una de esas apps estaba fundamentalmente haciendo lo mismo: buscar datos de una API, renderizarlos como HTML, y dejar que los usuarios envíen formularios. Habíamos inventado este pipeline increíblemente sofisticado, transpilación JSX, diffing de virtual DOM, manejo de estado, hidratación, para hacer lo que el browser ya hace nativamente con tags ` <a> ` y elementos ` <form> `.

La tesis de [HTMX](<https://htmx.org/>) es simple y casi ofensivamente obvia: **¿qué pasa si simplemente mandamos HTML por el cable?**

## HTMX 2.0: Cambios Relevantes

HTMX 2.0 (lanzado estable a fines de 2025) no es una revolución sobre 1.x, pero los refinamientos importan. Acá va lo que realmente es diferente:

### La Renovación de la Sintaxis ` hx-on `

Esta me afectó durante la migración. La sintaxis vieja:

```html
<button hx-post="/api/items"
 hx-on::after-request="handleResponse(event)">
 Guardar
</button>
```

Se convirtió en:

```html
<button hx-post="/api/items"
 hx-on:htmx:after-request="handleResponse(event)">
 Guardar
</button>
```

El doble dos-puntos causaba problemas con ciertos parsers HTML y políticas CSP. La nueva sintaxis es más explícita y, esto es importante, se lleva mejor con headers Content Security Policy que desactivan event handlers inline, ya que HTMX los registra programáticamente.

### Extensión WebSocket Nativa (Estable)

La extensión WebSocket salió de experimental en 2.0, y es genuinamente excelente:

```html
<div hx-ext="ws" ws-connect="/ws/notificaciones">
 <div id="feed-notificaciones" hx-swap-oob="beforeend">
 <!-- El servidor pushea fragmentos HTML aquí -->
 </div>
</div>
```

Del lado del servidor (ejemplo en Go):

```go
func handleWS(w http.ResponseWriter, r *http.Request) {
 conn, err := upgrader.Upgrade(w, r, nil)
 if err != nil {
 return
 }
 defer conn.Close()

 for notification := range userNotifications(r.Context()) {
 html := renderNotificationHTML(notification)
 conn.WriteMessage(websocket.TextMessage, []byte(html))
 }
}
```

Sin serialización JSON. Sin rendering de templates del lado del cliente. Sin sincronización de estado. El servidor manda un ` <div> ` con el contenido de la notificación, y HTMX lo mete en el DOM. Eso es todo.

### Server-Sent Events (La Joya Subestimada)

Para datos en tiempo real unidireccionales, SSE es casi siempre la opción correcta sobre WebSockets. HTMX 2.0 lo hace trivial:

```html
<div hx-ext="sse" sse-connect="/events/metricas-dashboard">
 <div sse-swap="cpu-update" hx-swap="innerHTML settle:300ms">
 <span class="metric">Cargando...</span>
 </div>
 <div sse-swap="memory-update" hx-swap="innerHTML">
 <span class="metric">Cargando...</span>
 </div>
 <div sse-swap="request-count" hx-swap="innerHTML">
 <span class="metric">Cargando...</span>
 </div>
</div>
```

Cada tipo de evento SSE se mapea a un target de swap diferente. El servidor envía eventos nombrados con payloads HTML, y cada atributo ` sse-swap ` atrapa el evento que le importa. Usé este patrón para todo el dashboard de monitoreo, gráficos en tiempo real incluidos. Los gráficos son SVGs renderizados en el servidor que se swapean. Sin Chart.js, sin D3 en el cliente.

## hx-boost: Progressive Enhancement Bien Hecho

` hx-boost ` es probablemente la feature que convence a los escépticos. Agregalo a tu body tag:

```html
<body hx-boost="true">
```

Y cada tag ` <a> ` y ` <form> ` en tu página es automáticamente "boosteado", los clicks y envíos son interceptados, el request se hace vía AJAX, y solo el contenido del ` <body> ` se swapea. La URL del browser se actualiza, el historial funciona, ir atrás y adelante funciona. Tu app multi-página se siente como una SPA con cero cambios de código.

Lo eficiente: si JavaScript no carga (o está desactivado), todo sigue funcionando como links y formularios normales. Eso no es un beneficio teórico, he tenido clientes en regiones con conexiones móviles inestables donde los bundles JS fallan en cargar entre 8 y 12% de las veces. Ese es usuarios igual tienen una app completamente funcional.

## Comparación Real: React vs HTMX para un Dashboard CRUD

Reconstruí la misma feature, una tabla de gestión de usuarios paginada, filtrable con edición inline, tanto en React (Next.js 15) como en HTMX (Go + templ). Acá van los números reales:

| Métrica | React (Next.js) | HTMX + Go |
| --- | --- | --- |
| JS enviado al cliente | 127KB gzipped | 14KB (htmx.min.js) |
| Tiempo hasta Interactivo (3G) | 3.1s | 0.8s |
| Tiempo de respuesta servidor (p95) | 45ms (API JSON) | 38ms (fragmento HTML) |
| Líneas de código cliente | 847 (JSX + hooks) | 12 (JS vanilla para diálogos de confirmación) |
| Líneas de código servidor | 203 (rutas API) | 341 (templates + handlers) |
| Paso de build | 34s (build Next.js) | 1.2s (compilación Go) |
| Dependencias de dev | 387 paquetes | 12 paquetes |

El tradeoff es claro: escribirs más código del lado del servidor con HTMX, pero dramáticamente menos del lado del cliente. ¿Código total? Más o menos lo mismo. Pero la versión HTMX tiene casi cero partes móviles en el cliente, lo que significa menos bugs en la parte del stack que es más difícil de depurar.

## El Patrón OOB Swaps: Actualizaciones Multi-Región

Los swaps out-of-band son la respuesta de HTMX a "pero ¿qué pasa con actualizar múltiples partes de la página?" Cuando el servidor responde a un request, puede incluir fragmentos HTML extra etiquetados con ` hx-swap-oob `:

```html
<!-- Respuesta primaria (se swapea en el target) -->
<tr id="user-42">
 <td>Nombre Actualizado</td>
 <td>updated@email.com</td>
</tr>

<!-- Out-of-band: también actualizar el conteo de notificaciones -->
<span id="notification-count" hx-swap-oob="true">3</span>

<!-- Out-of-band: también actualizar el timestamp de última modificación -->
<time id="last-modified" hx-swap-oob="true">Recién</time>
```

Una respuesta HTTP actualiza tres partes diferentes de la página. Sin manejo de estado del lado del cliente. Sin Redux. Sin Zustand. Sin signals. El servidor conoce el estado completo, renderiza todos los fragmentos HTML que cambiaron, y los manda en una sola respuesta.

Era escéptico con este patrón hasta que lo usé en producción. Es absurdamente efectivo para el 90% de los casos.

## Cuándo HTMX Es la Elección Equivocada

Sería deshonesto si no cubriera los case es donde todavía elegiría React:

**Edición colaborativa en tiempo real**, considerar Google Docs o Figma. La resolución de conflictos y transformaciones operacionales necesitan lógica del lado del cliente que no mapea a hypermedia.

**Aplicaciones offline-first**, HTMX necesita un servidor. Si tu app tiene que funcionar sin red, se necesita una capa de datos del lado del cliente.

**Interfaces complejas de drag-and-drop**, tableros Kanban, constructores de workflows. El modelo de interacción es fundamentalmente del lado del cliente.

**Visualizaciones de datos densas con interactividad**, zoom, paneo, brushing en gráficos. Los SVGs renderizados en servidor sirven para mostrar, pero la exploración interactiva necesita JS del cliente.

Para todo lo demás, y lo digo en serio, HTMX no es solo viable, muchas veces es la mejor opción.

## Estrategia de Migración: The Strangler Fig

No teners que reescribir todo. Migré el dashboard del cliente incrementalmente:

1. **Semana 1**: Agregué ` hx-boost ` al layout. Navegación tipo SPA instantánea, sin otros cambios.
2. **Semana 2-3**: Convertí las páginas CRUD más simples a HTML renderizado en servidor con atributos HTMX. Eliminé sus componentes React.
3. **Semana 4-5**: Ataqué los formularios complejos. Usé ` hx-target ` y ` hx-swap ` para validación inline y actualizaciones parciales.
4. **Semana 6**: Convertí el sistema de notificaciones en tiempo real de un cliente React + WebSocket a la extensión SSE de HTMX.
5. **Semana 7**: Eliminé React, ReactDOM, Next.js, y 374 paquetes npm del proyecto.

La app estuvo en producción todo el tiempo. Los usuarios no notaron el cambio.

## La Extensión Preload: Performance Percibido

Un patrón más que vale la pena compartir. La extensión preload de HTMX hace que la navegación se sienta instantánea:

```html
<body hx-boost="true" hx-ext="preload">
 <nav>
 <a href="/dashboard" preload="mousedown">Dashboard</a>
 <a href="/usuarios" preload="mousedown">Usuarios</a>
 <a href="/configuracion" preload="mousedown">Configuración</a>
 </nav>
</body>
```

` preload="mousedown" ` dispara el request en mousedown (que pasa ~100-150ms antes del evento click). Para cuando el click se registra y HTMX procesa el boost, la respuesta generalmente ya está cacheada. Combinado con tiempos de respuesta del servidor bajo 50ms, la navegación se siente literalmente instantánea.

## Reflexiones Finales

HTMX 2.0 no va a reemplazar a React para cada proyecto. Pero me forzó a confrontar una pregunta incómoda: ¿cuánta de la complejidad de mi stack frontend existe para resolver problemas que el browser ya resolvió en 1999?

La respuesta, para la mayoría de las apps que construyo, es "la mayor parte."

La web fue diseñada como un sistema hypermedia. HTMX simplemente se apoya en ese diseño en lugar de pelear contra él. Y después de seis meses construyendo con esto en producción, te puedo decir: no es un juguete. No es un paso atrás. Es una forma genuinamente diferente (y muchas veces mejor) de construir aplicaciones web.

Un proyecto de fin de semana es suficiente para evaluar el enfoque en un caso de uso concreto.

## Preguntas frecuentes

### ¿HTMX sirve para aplicaciones interactivas complejas?

Depende del patrón de interacción. HTMX brilla en apps CRUD-heavy, dashboards y sitios de contenido. Para UIs altamente interactivas como editores colaborativos o drag-and-drop complejo, se va a seguir necesitando un framework del lado del cliente, o un enfoque híbrido.

### ¿Se puede usar HTMX junto con componentes React o Vue?

Totalmente. HTMX 2.0 se lleva muy bien con arquitectura de islas. Puede usar HTMX para navegación a nivel página y data fetching mientras montás React/Vue para widgets interactivos específicos.

### ¿Cómo maneja HTMX el SEO comparado con SPAs?

Como HTMX sirve HTML real desde el servidor, el SEO funciona de una, sin hidratación SSR, sin hacks de prerendering. Los buscadores ven el mismo contenido que los usuarios.

## Tips

1. **Tip clave:** Utilizar ` hx-boost='true' ` en tu tag ` <body> ` para mejorar progresivamente cada enlace y formulario, cero JavaScript necesario para navegación.
2. **Ojo:** En HTMX 2.0 la sintaxis de ` hx-on ` cambió de ` hx-on::after-request ` a ` hx-on:htmx:after-request `. Los tutoriales viejos harán perder horas.
3. **Truco de rendimiento:** Combiná ` hx-push-url='true' ` con la extensión ` preload ` para navegación percibida instantánea que le compite a cualquier router SPA.
4. **Secreto SSE:** El atributo ` sse-swap ` con ` hx-swap='beforeend settle:500ms' ` te da actualizaciones en tiempo real sin una sola línea de JS del lado del cliente.

- htmx
- hypermedia
- frontend
- react
- ssr
- websockets
- sse
- spa-fatigue

[Volver al Blog](<https://lucioduran.com/es/blog>)
