Introducción a htmx 4

Por: Artiko
htmxhtmx4hipermediaintroduccion

Introducción a htmx 4

htmx es una librería que extiende el HTML para que cualquier elemento pueda hacer una petición HTTP y reemplazar cualquier parte de la página con el HTML que devuelve el servidor. Nada más. No hay componentes, no hay estado en el cliente, no hay build.

<button hx-post="/clicked" hx-target="#resultado" hx-swap="innerHTML">
  Haz clic
</button>
<div id="resultado"></div>

Ese botón hace POST /clicked, toma el HTML de la respuesta y lo mete dentro de #resultado. Con eso ya entendiste el 60% de htmx: el resto son matices sobre cuándo se dispara, a dónde va y cómo se inserta.

El problema que resuelve

El HTML original tiene una limitación arbitraria: sólo los enlaces y los formularios pueden hacer peticiones, sólo con GET y POST, y el resultado siempre reemplaza la página entera.

flowchart LR
    subgraph HTML["HTML nativo"]
        A["&lt;a&gt; y &lt;form&gt;"] --> B["GET / POST"] --> C["Reemplaza toda la página"]
    end
    subgraph HTMX["HTML + htmx"]
        D["Cualquier elemento"] --> E["GET/POST/PUT/PATCH/DELETE"] --> F["Reemplaza cualquier fragmento"]
    end

htmx completa las cuatro casillas que faltaban:

  1. Cualquier elemento puede disparar una petición, no sólo <a> y <form>.
  2. Cualquier verbo HTTP, no sólo GET y POST.
  3. Cualquier evento del DOM la puede disparar, no sólo el clic o el submit.
  4. Cualquier parte del DOM puede recibir la respuesta, no sólo el documento completo.

Aplicaciones dirigidas por hipermedia (HDA)

La arquitectura que propone htmx se llama Hypermedia-Driven Application. La regla central es: el servidor devuelve HTML, no JSON. El estado de la aplicación vive en el servidor y en el DOM; no hay una copia paralela en un store del cliente.

sequenceDiagram
    participant U as Usuario
    participant D as DOM
    participant H as htmx
    participant S as Servidor
    U->>D: clic en el botón
    D->>H: evento (hx-trigger)
    H->>S: fetch("/tareas", {method:"POST"})
    S-->>H: 200 text/html — fragmento
    H->>D: swap en el target
    D-->>U: UI actualizada

Comparado con una SPA:

AspectoSPA (React/Vue)HDA (htmx)
Formato de la respuestaJSONHTML
Dónde vive el estadoStore del cliente + servidorServidor + DOM
RenderizadoClienteServidor
BuildObligatorioNinguno
Duplicación de lógicaValidación y rutas en ambos ladosSólo en el servidor
Peso del runtime45–120 kB+~14 kB

Esto no es “SPA vs htmx” como pelea religiosa: es una decisión de dónde poner la complejidad. Si tu aplicación es un editor de video, un canvas colaborativo o un cliente offline, necesitas estado en el cliente y htmx no es la herramienta. Si tu aplicación es un CRUD, un panel, un e-commerce o cualquier cosa donde el servidor ya es la fuente de la verdad, htmx elimina toda una capa.

Qué es htmx 4: “The Fetchening”

htmx 1 nació con soporte para IE11 y por eso usaba XMLHttpRequest. htmx 2 lo mantuvo por compatibilidad. htmx 4 reescribe el núcleo sobre fetch(), y eso desbloquea todo lo demás:

flowchart TD
    F["fetch() en el core"] --> S["Streaming nativo<br/>ReadableStream"]
    F --> A["Aborto limpio<br/>AbortController"]
    F --> E["Extensiones pueden<br/>reemplazar el fetch"]
    F --> C["Menos código interno"]
    C --> M["Morphing en el core<br/>innerMorph / outerMorph"]
    C --> P["&lt;hx-partial&gt;"]

Los cambios que definen la versión 4:

1. fetch() reemplaza a XMLHttpRequest

Consecuencia directa: el modelo de eventos cambia (los eventos xhr:* desaparecen), el streaming es de primera clase y las extensiones pueden interceptar o sustituir la función de red.

2. La herencia de atributos ahora es explícita

En htmx 2, un hx-target en un <div> lo heredaban todos sus hijos automáticamente. En htmx 4 hay que pedirlo con el modificador :inherited.

<!-- htmx 2: herencia implícita -->
<div hx-confirm="¿Seguro?">
  <button hx-delete="/cuenta">Eliminar</button>
</div>

<!-- htmx 4: herencia explícita -->
<div hx-confirm:inherited="¿Seguro?">
  <button hx-delete="/cuenta">Eliminar</button>
</div>

Es el cambio que más código rompe y el que más problemas de depuración elimina a largo plazo.

3. Morphing en el core

Idiomorph era una extensión; ahora innerMorph y outerMorph son estrategias de swap nativas. Morphing significa que htmx compara el DOM viejo con el nuevo y toca sólo lo que cambió, en lugar de destruir y recrear: se conserva el foco, la posición del scroll, el estado de los <video> y de los widgets JS.

4. Los errores ahora hacen swap

En htmx 2, una respuesta 422 con el HTML de los errores de validación no se insertaba salvo que lo forzaras con un listener. En htmx 4 se insertan todos los códigos excepto 204 y 304, y hay un atributo dedicado, hx-status, para afinar el comportamiento por código.

5. El historial ya no cachea el DOM

htmx 2 guardaba snapshots del DOM en localStorage para el botón “atrás”, con todos los problemas de tamaño e invalidación que eso implica. htmx 4 simplemente vuelve a pedir el contenido a la red. Quien quiera el comportamiento anterior usa la extensión hx-history-cache.

6. Renombres importantes

htmx 2htmx 4Qué hace
hx-disablehx-ignoreNo procesar htmx en ese subárbol
hx-disabled-elthx-disableDeshabilitar elementos durante la petición
htmx:afterRequesthtmx:after:requestEvento posterior a la petición
defaultSwapStyledefaultSwapConfig del swap por defecto

Ese cruce de hx-disable es la trampa más peligrosa de la migración: el atributo existe en ambas versiones y significa cosas distintas. Lo cubrimos a fondo en el capítulo 17.

7. Novedades que no existían

  • <hx-partial>: un elemento que declara su propio target y swap, para actualizar varias zonas de la página desde una sola respuesta sin la gimnasia de los OOB swaps.
  • hx-status: comportamiento por código HTTP.
  • hx-optimistic: UI optimista declarativa.
  • hx-live: bindings reactivos sobre atributos, clases, texto y estilos.
  • htmx.timeout(): una promesa que resuelve tras un intervalo, usable dentro de hx-on.
  • HCON: una notación compacta para configuración en atributos.

Estado de la versión

RamaEstado hoyUso recomendado
1.xSoportada, con IE11Sólo mantenimiento legado
2.xEstable, latest en npmProducción hoy
4.0Beta (4.0.0-beta6)Proyectos nuevos que toleran beta; aprendizaje

El equipo de htmx ha declarado soporte perpetuo para la 2.x, así que migrar es una decisión, no una obligación. No hay htmx 3: el salto de número es deliberado para señalar la magnitud del cambio interno.

Qué necesitas para seguir el curso

Un editor, un navegador y cualquier servidor que devuelva HTML. Los ejemplos del curso usan Bun porque son tres líneas, pero puedes traducirlos a lo que uses:

// servidor.js — el "backend" mínimo del curso
Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/clicked") {
      return new Response("<p>¡Hola desde el servidor!</p>", {
        headers: { "Content-Type": "text/html" },
      });
    }
    return new Response(Bun.file("./index.html"));
  },
});

En el capítulo 2 lo montamos completo y hacemos la primera petición real.

Resumen

  • htmx extiende el HTML para que cualquier elemento haga peticiones y actualice cualquier fragmento.
  • El servidor devuelve HTML; el estado vive en el servidor y en el DOM.
  • htmx 4 reescribe el core sobre fetch(), lo que trae streaming, morphing y partials nativos.
  • La herencia de atributos pasa a ser explícita con :inherited.
  • Los errores HTTP ahora se insertan por defecto, con hx-status para afinarlo.
  • La versión actual es 4.0.0-beta6; la estable sigue siendo 2.x.

Siguiente: Instalación y primer proyecto →.