10 puntos por GN⁺ 2025-10-11 | 2 comentarios | Compartir por WhatsApp
  • Al usar HTMX, pude reducir la cantidad de código en alrededor de un 70%, pero también experimenté problemas de sincronización entre interfaces y un aumento en la complejidad de la gestión de estado del frontend
  • Después de adoptar Datastar, desarrollar aplicaciones multiusuario en tiempo real se volvió más fácil de mantener y con código más conciso, sin necesidad de WebSockets
  • Mientras que HTMX distribuye la lógica de comportamiento en torno a atributos HTML, Datastar mejora la consistencia y mantenibilidad de la lógica mediante un modelo de actualización guiado por el servidor
  • La API de Datastar tiene menos atributos y da la sensación de mejorar la legibilidad y productividad del código
  • Datastar aprovecha activamente tecnologías nativas de la web como Server-Sent Events (SSE), Web Components y CSS View Transitions, lo que hace posible la colaboración en tiempo real y una estructura de componentes reutilizables

Introducción y motivación

  • En 2022, David Guillot compartió en DjangoCon Europe un caso en el que migró un SaaS basado en React a HTMX, redujo el código en aproximadamente un 70% y mejoró funcionalidades
  • Después de eso, muchos equipos experimentaron que, al pasar de una app de página única (SPA) a una aplicación hipermedia multipágina, se reducía el código y mejoraban tanto la experiencia de desarrollo como la experiencia de usuario
  • El autor también confirmó, al migrar un proyecto de HTMX a Datastar, que el código se volvió más corto y que era posible desarrollar una app multiusuario en tiempo real sin WebSocket ni gestión de estado compleja

Problemas que motivaron el cambio

  • Mientras preparaba una charla de FlaskCon 2025, intentó sincronizar la UI combinando HTMX y AlpineJS, pero se encontró con problemas de sincronización de interfaz
    • Como ambas librerías son herramientas separadas creadas por desarrolladores distintos, no pueden comunicarse entre sí, por lo que el desarrollador tiene que encargarse manualmente de integrarlas
    • Inicializar componentes en distintos momentos y coordinar eventos requirió mucho más código y tiempo de depuración de lo esperado
  • Le llamó la atención que Datastar integrara las funciones de ambas librerías y aun así se entregara en menos de 11 KB, así que decidió probarlo
    • Esto favorece el rendimiento de carga de página para usuarios en dispositivos móviles

Un mejor diseño de API en Datastar

  • La API de Datastar se siente mucho más ligera que la de HTMX, y requiere menos atributos adicionales para obtener el resultado deseado
  • HTMX necesita varios atributos en la mayoría de las interacciones
    • Definir la URL, especificar el elemento objetivo y configurar cómo se procesa la respuesta se hace con atributos separados
    • Normalmente se usan 2 o 3 atributos cada vez, y a veces hay que seguir la cadena de herencia para entender cómo se comportan
    <a hx-target="#rebuild-bundle-status-button"  
       hx-select="#rebuild-bundle-status-button"  
       hx-swap="outerHTML"  
       hx-trigger="click"  
       hx-get="/rebuild/status-button"></a>  
    
  • Datastar normalmente implementa la misma funcionalidad con un solo atributo
    <a data-on-click="@get('/rebuild/status-button')"></a>  
    
    • Incluso meses después, al volver a ver el código, es fácil entender cómo funciona

Diferencias en la forma de operar

  • Mientras que HTMX es una librería de frontend cuyo objetivo es extender la especificación HTML, Datastar es una librería guiada por el servidor cuyo objetivo es construir aplicaciones de actualización en tiempo real de alto rendimiento usando tecnologías nativas de la web
  • HTMX define el comportamiento agregando atributos al elemento que dispara la solicitud, y aunque actualice elementos muy alejados en la página, la lógica queda dispersa en varias capas
  • Datastar hace que el servidor decida qué debe cambiar, concentrando toda la lógica de actualización en un solo lugar
  • Ejemplo de HTMX

    <div>  
      <div id="alert"></div>  
        <button hx-get="/info"   
                hx-select="#info-details"   
                hx-swap="outerHTML"  
                hx-select-oob="#alert">  
            Get Info!  
        </button>  
    </div>  
    
    • Al presionar el botón, se envía una solicitud GET a /info, se reemplaza el botón con el elemento del ID info-details de la respuesta, y se reemplaza el elemento con ID alert de la página por el de la respuesta
    • El botón necesita saber demasiada información y además debe anticipar qué devolverá el servidor, lo que debilita el principio de HTMX de "localidad del comportamiento (locality of behavior)"
  • Enfoque mejorado de Datastar

    <div>  
        <div id="alert"></div>  
        <button id="info-details"  
        data-on-click="@get('/info')">  
            Get Info!  
        </button>  
    </div>  
    
    • El servidor devuelve una cadena HTML que incluye dos elementos raíz con los mismos ID
      <p id="info-details">These are the details you are looking for…</p>  
      <div id="alert">Alert! This is a test.</div>  
      
    • Es una opción simple y con buen rendimiento

Pensar a nivel de componente

  • Un mejor enfoque es tratar el HTML como componentes
  • Identificar la esencia de ese componente
    • Cómo obtiene el usuario información adicional sobre un elemento específico
    • Cuando el usuario hace clic en el botón, aparece la información o, si no la hay, se renderiza un error; en cualquier caso, el componente pasa a un estado estático
  • Separar componentes por estado

    • Estado de placeholder:
      <!-- info-component-placeholder.html -->  
      <div id="info-component">  
          <button data-on-click="@get('/product/{{product.id}}/info')">  
              Get Info!  
          </button>  
      </div>  
      
    • Estado con información mostrada:
      <!-- info-component-get.html -->  
      <div id="info-component">  
          {% if alert %}<div id="alert">{{ alert }}</div>{% endif %}  
          <p>{{product.additional_information}}</p>  
      </div>  
      
    • Cuando el servidor renderiza el HTML, Datastar actualiza automáticamente la página
    • Pensar a nivel de componente evita entrar en estados incorrectos o perder el estado del usuario

Actualizar varios componentes al mismo tiempo

  • Uno de los puntos impresionantes de la charla de David Guillot fue que, cuando la app actualizaba la cantidad de favoritos, también se actualizaba un contador ubicado muy lejos del componente modificado
    • HTMX dispara un evento JavaScript, que a su vez dispara una solicitud GET desde un componente remoto
  • Datastar permite actualizar varios componentes al mismo tiempo incluso dentro de una función síncrona
  • Ejemplo del carrito de compras

    • Componente para agregar al carrito:
      <form id="purchase-item"  
            data-on-submit="@post('/add-item', {contentType: 'form'})">"  
      >  
        <input type=hidden name="cart-id" value="{{cart.id}}">  
        <input type=hidden name="item-id" value="{{item.id}}">  
        <fieldset>  
          <button data-on-click="$quantity -= 1">-</button>  
          <label>Cantidad  
            <input name=quantity type=number data-bind-quantity value=1>  
          </label>  
          <button data-on-click="$quantity += 1">+</button>  
        </fieldset>  
        <button type=submit>Agregar al carrito</button>  
        {% if msg %}  
          <p class=message>{{msg}}</p>  
        {% endif %}  
      </form>  
      
    • Componente de visualización del conteo del carrito:
      <div id="cart-count">  
          <svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">  
              <use href="#shoppingCart">  
          </svg>  
          {{count}}  
      </div>  
      
    • En Django, ambos componentes se actualizan en la misma solicitud:
      from datastar_py.consts import ElementPatchMode  
      from datastar_py.django import (  
          DatastarResponse,  
          ServerSentEventGenerator as SSE,  
      )  
      
      def add_item(request):  
          # se omite la actualización importante del estado  
          return DatastarResponse([  
              SSE.patch_elements(  
                  render_to_string('purchase-item.html', context=dict(cart=cart, item=item, msg='Item added!'))  
              ),  
              SSE.patch_elements(  
                  render_to_string('cart-count.html', context=dict(count=item_count))  
              ),  
          ])  
      

Filosofía web nativa

  • A través de la comunidad de Datastar en Discord, el autor entendió que Datastar no es solo un script helper, sino una filosofía para construir apps aprovechando los primitivos básicos de la web
  • Mientras HTMX busca hacer evolucionar la especificación HTML, Datastar está más interesado en fomentar la adopción de capacidades nativas de la web
    • CSS view transitions
    • Server-Sent Events
    • Web Components, entre otros
  • Un gran logro fue refactorizar componentes complejos de AlpineJS para extraer Web Components simples y reutilizarlos en varios lugares
  • Es un excelente patrón para lograr alta localidad del comportamiento y reutilización mediante la creación de elementos HTML personalizados, sin herramientas como React

Actualizaciones en tiempo real para apps multiusuario

  • Las apps con colaboración como función de primera clase se diferencian de otras, y Datastar resuelve ese reto
  • La mayoría de los desarrolladores con HTMX obtienen información del servidor mediante polling o escriben código WebSocket personalizado, aumentando la complejidad
  • Datastar usa una tecnología web simple llamada Server-Sent Events (SSE) para que el servidor empuje actualizaciones a los clientes conectados
    • Cuando un usuario agrega un comentario o cambia un estado, el servidor actualiza el navegador de inmediato, con muy poco código adicional
    • Permite construir dashboards en tiempo real, paneles de administración y herramientas colaborativas sin JavaScript personalizado
  • Si la conexión del cliente se interrumpe, el navegador intenta reconectarse automáticamente, sin requerir código adicional
    • También puede informar al servidor sobre el último evento recibido

Evitar la complejidad excesiva

  • La comunidad de Datastar en Discord ayudó a entender la visión de Datastar sobre cómo crear aplicaciones web
    • Actualizaciones de UI basadas en push
    • Reducción de complejidad
    • Manejo de situaciones localmente complejas usando herramientas como Web Components
  • La comunidad también ayuda a que los nuevos usuarios se den cuenta cuando están abordando el problema de forma innecesariamente compleja

Consejos clave

  • No temas volver a renderizar el componente completo y enviarlo
    • Es más fácil y no tiene un gran impacto en el rendimiento
    • Puede ofrecer mejor compresión, y el navegador es muy rápido parseando cadenas HTML
  • El servidor es la fuente de verdad del estado y es más poderoso que el navegador
    • Deja que el servidor maneje la mayor parte del estado; quizá necesites menos señales reactivas de las que imaginas
  • Los Web Components son excelentes para encapsular lógica dentro de elementos personalizados con alta localidad del comportamiento
    • La animación del campo de estrellas del encabezado del sitio web de Datastar es un buen ejemplo
    • El elemento <ds-starfield> encapsula todo el código de la animación del campo de estrellas y expone tres atributos para cambiar su estado interno
    • Datastar controla esos atributos cuando cambia un input de rango o cuando el mouse se mueve sobre el elemento

Posibilidades que van más allá de los límites

  • Lo más emocionante es el potencial que Datastar hace posible
  • La comunidad crea regularmente proyectos que superan por mucho los límites que suelen encontrar los desarrolladores con otras herramientas

Casos destacados

  • El demo de monitoreo de base de datos en la página de ejemplos
    • Aprovecha Hypermedia para mejorar de forma importante la velocidad y el uso de memoria frente a demos presentados en conferencias de JavaScript
  • 1 Billion Checkboxes de Anders Murphy
    • Cuando el experimento de 1 millón de checkboxes superó la capacidad del servidor, usó Datastar para implementar 1 mil millones en un servidor barato
  • Una app web que muestra los datos de todas las estaciones de radar de Estados Unidos
    • Cuando cambia la señal de un radar, el punto correspondiente en la UI cambia en menos de 100 milisegundos
    • Se actualizan más de 800 mil puntos por segundo, y el usuario puede desplazarse hasta 1 hora hacia atrás con una latencia de menos de 700 milisegundos
    • Que esto sea posible como app de Hypermedia muestra lo que Datastar puede habilitar

Experiencia actual de uso

  • Aún está en una etapa de exploración de Datastar y puede implementar rápida y fácilmente el manejo AJAX de actualizaciones de UI típico de HTMX
  • Está aprendiendo y experimentando con varios patrones para lograr más cosas con Datastar
  • Desde hace décadas le interesa cómo ofrecer mejores experiencias de usuario con actualizaciones en tiempo real, y le gusta que Datastar permita actualizaciones basadas en push incluso en código síncrono
  • Cuando empezó a usar HTMX sintió una gran alegría, pero desde que cambió a Datastar siente que no perdió nada y, por el contrario, ganó muchísimo más
  • Si usar HTMX te dio esa alegría, con Datastar probablemente sentirás ese mismo salto otra vez, como redescubrir lo que la web siempre debió hacer

2 comentarios

 
GN⁺ 2025-10-11
Comentarios en Hacker News
  • Aprecio que Chris salga de su zona de confort, se atreva a probar algo nuevo y comparta esa experiencia con nosotros. Puede que yo esté algo sesgado porque llevo 4 años creando web apps con htmx, pero creo que aquí se hace visible la diferencia arquitectónica principal entre Datastar y htmx: htmx está guiado por HTML, mientras que Datastar está guiado por el servidor. Es cierto que la API del lado del cliente es más simple, pero eso ocurre porque la lógica del servidor se vuelve más compleja. Por ejemplo, si un elemento HTML no tiene información sobre dónde insertar un fragmento devuelto por el servidor, entonces esa información tiene que quedar registrada del lado del servidor, así que la complejidad necesariamente existe en alguno de los dos lados. La elección de arquitectura parece ser una cuestión de preferencia. La lógica de “less attributes” del ejemplo tampoco me parece 100% justa, porque incluye atributos opcionales en htmx; por ejemplo, si quitas hx-trigger="click", ya reduces un 20% de los atributos. Y si además se escribiera un HTML más accesible, usando <button> en vez de <span>, por ejemplo, daría más confianza. Al final, la gran fortaleza de Datastar parece ser que viene con funcionalidades tipo Alpine o Stimulus integradas, y eso sí me parece realmente impresionante
    • Me da la impresión de que con Datastar la complejidad baja bastante porque no hace falta implementar por separado capacidades de eventos para actualizar en tiempo real otras partes de la página; puedes bajar todo de una vez y refrescarlo. Claro, dependiendo del caso, un enfoque basado en eventos o en carga diferida también podría ser mejor
    • Vi el comentario de que “es como si Alpine o Stimulus vinieran integrados en HTMX”, y eso me hizo pensar en usar HTMX para un proyecto personal. Me pregunto si hay material que explique que también hace falta una librería adicional como AlpineJS o Stimulus
    • Hubo una discusión sobre que, si el elemento HTML no indica dónde insertar el fragmento, entonces el servidor tiene que saberlo. Pero también me hace pensar que el frontend sería más ligero y rápido así, ¿no? Sobre todo si hay muchísimos elementos
    • Esta estructura se parece un poco al framework Seaside de Pharo. En mi empresa hacíamos apps B2B con Pharo y, como el estado de la UI se manejaba en el backend, había mucho ida y vuelta entre frontend y backend. Para B2B, donde lo importante no es tanto el tiempo real o la latencia, funciona bien, pero no encaja en apps B2C que necesiten escalar mucho
  • Habiendo usado tanto Datastar como HTMX, todavía no tengo claro cuál sería la gran diferencia al construir una app con Datastar. Uso FastAPI, HTMX, Alpine.js y SSE juntos para cosas como mostrar logs en tiempo real y actualizar el estado de despliegues. Al ver los ejemplos de Datastar, no logro notar qué parte sería realmente más simple que esta estructura. (Referencia de código: devpush SSE partial). También probé Web Components cuando desarrollaba Basecoat, pero por problemas de estilos, manejo de estado y otras razones, terminé regresando al HTML/CSS/JS tradicional. devpu.sh, basecoatui.com
    • Incluso en HTMX, cuando piensas en extensibilidad funcional como en Datastar, muchas veces termina siendo más simple refrescar la lista completa de una sola vez. En vez de intentar actualizar el estado de cada despliegue individualmente, actualizar toda la lista evita preocuparte por casos límite como la paginación, así que el resultado es mucho más simple y el código queda más liviano
  • Para quienes piensan que Datastar se queda corto para tiempo real/colaboración/multijugador, quiero compartir tres demos que funcionan sin funciones PRO e incluso corren en un VPS de 5 dólares, y que además llegaron a la portada de HN. Muestran lo bien hecho que está Datastar: Checkboxes, Cells, Game of Life Example. Los ejemplos de checkboxes y cells tienen un renderizado de vista bastante dinámico, permiten hacer bastante zoom out y además tienen backpressure para scrolling virtual
    • Si entendí bien cómo está estructurado el código, en realidad no parece que usen el enfoque de diff/patch que recomienda datastar, sino que rerenderizan la página completa cada vez. De hecho, ese modelo mental me resulta más atractivo porque parece mucho más simple que los ejemplos donde el servidor rastrea el estado del cliente con gran detalle. Me pregunto si apps complejas en general podrían construirse así. Si alguien tiene algún texto de referencia sobre cómo manejar rerenderizado inmediato mientras se rastrea el estado de varios widgets cuando los usuarios navegan entre distintas páginas, me gustaría verlo
    • En los ejemplos de checkboxes/cells mencionaron que se puede hacer “zoom out”, pero me gustaría saber exactamente cómo se hace. Y también habría estado bien tener una opción tipo data-replace-url que actualizara automáticamente la URL de la vista actual con esas coordenadas (x=123&y=456, etc.)
    • Viendo la mención de las funciones PRO, me enteré de que usan un modelo open core (una parte open source y otra de pago, con licencia de 299 dólares). Yo paso
  • Hace poco leí este texto (htmx, datastar, greedy developer) y entendí que varias de las buenas funciones clave de Datastar se fueron a la versión de pago (Pro). Aunque quiera apoyar financieramente a un framework, sea open source o de pago, este tipo de precedente me preocupa un poco
    • Yo también venía siguiéndole la pista a Datastar desde hace meses y esperaba el lanzamiento 1.0.0, pero ya se me apagó por completo el entusiasmo. Me ha pasado demasiadas veces lo de “es open source, pero en realidad no”, así que ya no me inspira confianza
    • En realidad yo había escrito antes que Datastar no me gustaba mucho, pero esta vez me dan ganas de defenderlo un poco. El creador del framework publicó su código gratis bajo licencia MIT, así que las funciones que antes liberó gratis siguen pudiéndose usar bajo MIT. Si lo has usado sin contribuir, depender de versiones anteriores también es una elección tuya. Que ahora cambie a un modelo de pago es decisión del dueño del producto, y si hace falta, siempre se puede hacer un fork
    • Pagué una sola vez los 299 dólares de la licencia PRO, pero todavía no he usado realmente ninguna función PRO. Quería hacer un clon de Google Sheets, pero incluso sin PRO fue suficiente para implementarlo. (ver demo de cells)
    • Siempre me dio la impresión de que la gente de Datastar iba más allá de simplemente promocionar lo bueno que era Datastar dentro del Discord de HTMX, y a veces sonaban algo agresivos. También vi en reddit comentarios con un tono de “si la beta forever ya tiene todo lo que necesitas, úsala; el open source no le debe nada a nadie”
    • Usando Datastar me vino inmediatamente a la mente el viejo caso de Meteor.js. (discusión de Meteor.js en HN)
  • No termino de entender el código de ejemplo de este post. Por ejemplo, este ejemplo con htmx: <span hx-target="#rebuild-bundle-status-button" hx-select="#rebuild-bundle-status-button" hx-swap="outerHTML" hx-trigger="click" hx-get="/rebuild/status-button"></span> parece convertirse en este código de datastar: <span data-on-click="@get('/rebuild/status-button')"></span> Y los otros ejemplos me confunden todavía más. Al final, no entiendo por qué alguien se cambiaría de htmx a Datastar
    • Básicamente, en HTMX eso significa “cuando se haga clic en este span, trae HTML desde /rebuild/status-button, extrae el elemento #rebuild-bundle-status-button del HTML devuelto y reemplaza el elemento actual”. En cambio, en Datastar sería “al hacer clic en el span, sigue directamente las instrucciones de /rebuild/status-button”. Si el servidor devuelve elementos con varios ID, Datastar identifica todos esos elementos y los reemplaza automáticamente. O sea, no hace falta usar target, select ni swap: con solo poner los ID ya funciona como se espera
    • La estructura de Datastar concentra la lógica en el backend. Es como el HTML tradicional de antes, donde haces una petición, recibes HTML y el navegador renderiza, pero en Datastar, una vez que la página ya cargó, cada interacción envía una petición al backend y solo recibe los cambios necesarios para aplicarlos. Es la estructura opuesta a una SPA donde el frontend no tiene lógica y el backend maneja todo el estado. En esencia, vuelve a aparecer el mismo tema de siempre: cómo repartir la lógica entre backend y frontend. Datastar permite concentrar la lógica en el servidor y aun así mantener una interfaz dinámica
    • Pero igual me pregunto por qué usan un span como elemento clicable. Un button o un enlace parecerían más apropiados
  • Irónicamente, el tema de este artículo es hipermedia, pero ni siquiera incluye un enlace al sitio oficial de Datastar. Aquí está: https://data-star.dev/
  • Una de las grandes ventajas de HTMX es que el cliente no necesita conocer la estructura de los datos que devuelve el servidor, pero si el cliente tiene que saber los ID o el significado de elementos individuales, siento que esa promesa se rompe. Claro, en la práctica muchos proyectos usan OOB (Out of Band), así que tampoco es que la separación estructural perfecta exista en el mundo real. Ojalá aparezca una forma de quedarse con lo mejor de ambos mundos
    • En la práctica, el cliente no necesita saber nada. El cliente ejecuta una acción y el servidor puede devolver la vista completa de la página. El cliente simplemente la renderiza de inmediato. Es parecido al modelo de immediate mode en los videojuegos
  • Esta forma en que Datastar aplica parches de HTML desde el servidor no me parece buena desde el punto de vista de separación de responsabilidades, y cuanto más grande sea la app, más engorroso se volvería estar inyectando partes de HTML desde el servidor todo el tiempo
    • Pero tampoco es que sea mejor si JS anda por todos lados inyectando HTML en fragmentos
    • Diseñar endpoints que sirvan pedazos de HTML desde el servidor me da cierta mala espina
  • Datastar me da la impresión de estar más pulido que htmx. He creado con éxito varios proyectos con htmx, pero siempre me quedó la espina de tener que añadir JS pegamento para manejar eventos, especialmente con AlpineJS y similares. Si Datastar de verdad reduce también esa necesidad, me entusiasma bastante
    • Recomiendo leer el ensayo grugs around the fire del sitio de Datastar
  • Yo llegué relativamente tarde a la tendencia de hipermedia, pero primero usé Datastar y últimamente me pasé a HTMX. La API de Datastar sí me parece un poco mejor, pero desde que htmx 2.0 soporta actualizaciones OOB (Out-Of-Band), en la mayoría de los casos termino inclinándome por htmx
    • Me llamó la atención eso de “llegué tarde a la hipermedia”. Es interesante recordar que Ted Nelson acuñó el término en 1965, y que entonces dijo “hyperfilm— a browsable or vari-sequenced movie— is only one of the possible hypermedia that require our attention”. Ver artículo relacionado
    • Lo que me incomoda al manejar elementos OOB en HTMX: 1. si es OOB, htmx-swap-oob="true" es obligatorio; si falta ese atributo, no funciona como uno espera 2. por el contrario, si no es OOB, entonces si tiene htmx-swap-oob="true", se ignora o se comporta mal. Por eso, cuando quieres reutilizar el mismo componente como OOB y no OOB, terminas teniendo que enviar siempre un flag isOob desde el servidor, y eso es bastante molesto
    • A mí me gusta más la API de alpine-ajax. Solo especificas varios targets y reemplaza cada elemento de forma consistente, sin JS. El concepto de signal/state de Datastar me parece que más bien añade complejidad y no me convence en absoluto