HTMX

HTML con superpoderes - AJAX, transiciones CSS, actualizaciones DOM via atributos, cero JavaScript necesario

TL;DR

En resumen: htmx da superpoderes a HTML - haz peticiones AJAX, dispara transiciones CSS y actualiza el DOM, todo desde atributos HTML.

Fortalezas principales:

  • Cero JavaScript necesario - UIs dinámicas con solo atributos HTML
  • El servidor devuelve HTML - sin APIs JSON, arquitectura más simple
  • Funciona con cualquier backend - Python, Go, Ruby, PHP, cualquiera
  • Tamaño diminuto - ~14kB min.gz’d, sin paso de build

Core Concepts

Concept 1: AJAX from HTML

Cualquier elemento puede hacer peticiones HTTP:

<!-- El clic dispara GET, la respuesta reemplaza #result -->
<button hx-get="/api/data" hx-target="#result">
  Cargar datos
</button>
<div id="result"></div>

<!-- Envío de formulario via AJAX -->
<form hx-post="/submit" hx-target="#message">
  <input name="email" type="email">
  <button>Enviar</button>
</form>
<div id="message"></div>

Concept 2: Server Returns HTML

A diferencia de SPAs, tu servidor devuelve fragmentos HTML:

<!-- Respuesta del servidor para GET /api/users -->
<ul>
  <li>Alice</li>
  <li>Bob</li>
</ul>

<!-- Respuesta del servidor para POST /submit -->
<p class="success">¡Gracias por suscribirte!</p>

Concept 3: Swap Strategies

Controla cómo las respuestas actualizan el DOM:

<!-- Reemplazar HTML interno (por defecto) -->
<div hx-get="/content" hx-swap="innerHTML">

<!-- Reemplazar elemento completo -->
<div hx-get="/content" hx-swap="outerHTML">

<!-- Añadir al elemento -->
<div hx-get="/items" hx-swap="beforeend">

<!-- Eliminar elemento después de la petición -->
<button hx-delete="/item/1" hx-swap="delete">Eliminar</button>

Quick Start

Add via CDN

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/[email protected]"></script>
</head>
<body>
  <button hx-get="/hello" hx-target="#output">
    Decir hola
  </button>
  <div id="output"></div>
</body>
</html>

Server Endpoint (any language)

# Ejemplo Python/Flask
@app.route('/hello')
def hello():
    return '<p>¡Hola desde el servidor!</p>'

Gotchas

Trigger events

<!-- Disparar en diferentes eventos -->
<input hx-get="/search"
       hx-trigger="keyup changed delay:300ms"
       hx-target="#results">

<!-- Cargar al cargar la página -->
<div hx-get="/stats" hx-trigger="load"></div>

<!-- Consultar cada 5 segundos -->
<div hx-get="/updates" hx-trigger="every 5s"></div>

<!-- Disparar en intersección (lazy load) -->
<img hx-get="/image" hx-trigger="revealed">

Loading indicators

<button hx-get="/slow-api" hx-indicator="#spinner">
  Cargar
</button>
<span id="spinner" class="htmx-indicator">Cargando...</span>

<style>
  .htmx-indicator { opacity: 0; transition: opacity 200ms; }
  .htmx-request .htmx-indicator { opacity: 1; }
</style>

Sending values

<!-- Incluir valores extra -->
<button hx-post="/action"
        hx-vals='{"id": 123, "action": "delete"}'>
  Eliminar
</button>

<!-- Incluir valores de input -->
<input name="search" hx-get="/search"
       hx-include="[name='filter']"
       hx-target="#results">

Response headers for control

# El servidor puede controlar el comportamiento de htmx
response.headers['HX-Redirect'] = '/new-page'
response.headers['HX-Refresh'] = 'true'
response.headers['HX-Trigger'] = 'itemDeleted'

When to Use

Ideal para:

  • Añadir interactividad a apps renderizadas en servidor
  • Proyectos Django, Rails, Laravel, Go
  • Equipos que quieren arquitectura simple
  • Reemplazar patrones jQuery AJAX

No ideal para:

  • SPAs altamente interactivas (usa React/Vue)
  • Aplicaciones offline-first
  • Gestión de estado client-side compleja
  • Apps colaborativas en tiempo real

Comparación:

FeaturehtmxReactAlpine.js
EnfoqueHypermediaSPALigero
Servidor devuelveHTMLJSONN/A
Paso de buildNoNo
Curva de aprendizajeFácilMediaFácil

Next Steps

Cheatsheet

AtributoPropósito
hx-getPetición GET
hx-postPetición POST
hx-putPetición PUT
hx-deletePetición DELETE
hx-targetDónde poner la respuesta
hx-swapCómo intercambiar (innerHTML, outerHTML, beforeend)
hx-triggerCuándo disparar (click, load, every 5s)
hx-indicatorElemento indicador de carga
hx-valsValores extra para enviar
hx-confirmDiálogo de confirmación