10 points par GN⁺ 2025-10-11 | 2 commentaires | Partager sur WhatsApp
  • Avec HTMX, il a été possible de réduire le volume de code d’environ 70 %, mais cela s’est accompagné de problèmes de synchronisation entre interfaces et d’une complexité accrue de la gestion d’état frontend
  • Après l’adoption de Datastar, le développement d’applications temps réel multi-utilisateurs est devenu possible avec un code plus concis et une maintenance plus simple, sans WebSockets
  • Alors que HTMX disperse la logique de fonctionnement autour des attributs HTML, Datastar améliore la cohérence et la maintenabilité de la logique grâce à un modèle de mise à jour piloté par le serveur
  • L’API de Datastar comporte moins d’attributs, ce qui donne une impression de meilleure lisibilité du code et de productivité accrue
  • Datastar s’appuie activement sur des technologies natives du web comme les Server-Sent Events (SSE), les Web Components et les CSS View Transitions, ce qui permet la collaboration en temps réel et une architecture en composants réutilisables

Introduction et motivation

Les problèmes qui ont déclenché la transition

  • En préparant une présentation à FlaskCon 2025, l’auteur a tenté de synchroniser l’UI en combinant HTMX et AlpineJS, mais s’est heurté à des problèmes de synchronisation
    • Les deux bibliothèques sont des outils distincts créés par des développeurs différents et ne peuvent pas communiquer entre elles, si bien que le développeur doit lui-même se charger de l’intégration
    • Entre l’initialisation des composants à différents moments et la coordination des événements, cela a demandé bien plus de code et de temps de débogage que prévu
  • L’auteur a alors essayé Datastar, remarquant qu’il réunissait les fonctions des deux bibliothèques tout en restant sous les 11 KB
    • Un avantage pour améliorer les performances de chargement sur les appareils mobiles

Une meilleure conception d’API avec Datastar

  • L’API de Datastar donne une impression bien plus légère que celle de HTMX, avec moins d’attributs à ajouter pour obtenir le résultat voulu
  • HTMX exige plusieurs attributs dans la plupart des interactions
    • définition de l’URL, désignation de l’élément cible, méthode de traitement de la réponse, etc., chacun via un attribut séparé
    • En pratique, on utilise souvent 2 à 3 attributs à chaque fois, et il faut parfois remonter une chaîne d’héritage pour comprendre leur comportement
    <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>  
    
  • Avec Datastar, un seul attribut suffit généralement pour implémenter la même fonctionnalité
    <a data-on-click="@get('/rebuild/status-button')"></a>  
    
    • Même plusieurs mois plus tard, il reste facile de comprendre le fonctionnement du code en le relisant

Différence de fonctionnement

  • HTMX est une bibliothèque frontend qui vise à étendre la spécification HTML, tandis que Datastar est une bibliothèque pilotée par le serveur qui cherche à construire des applications temps réel performantes et natives du web
  • HTMX définit le comportement en ajoutant des attributs à l’élément qui déclenche la requête, ce qui disperse la logique entre plusieurs couches, même lorsqu’il s’agit de mettre à jour un élément éloigné dans la page
  • Datastar laisse le serveur décider de ce qu’il faut modifier, ce qui centralise toute la logique de mise à jour au même endroit
  • Exemple 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>  
    
    • Quand on clique sur le bouton, une requête GET est envoyée à /info, le bouton est remplacé par l’élément de l’ID info-details dans la réponse, et l’élément de la page portant l’ID alert est remplacé par l’élément du même ID dans la réponse
    • Le bouton doit connaître trop d’informations, et comme il faut aussi savoir à l’avance ce que le serveur va renvoyer, le principe de « locality of behavior » de HTMX s’en trouve affaibli
  • Approche améliorée de Datastar

    <div>  
        <div id="alert"></div>  
        <button id="info-details"  
        data-on-click="@get('/info')">  
            Get Info!  
        </button>  
    </div>  
    
    • Le serveur renvoie une chaîne HTML contenant deux éléments racine portant les mêmes ID
      <p id="info-details">These are the details you are looking for…</p>  
      <div id="alert">Alert! This is a test.</div>  
      
    • Une option simple et très performante

Penser au niveau du composant

  • Une meilleure approche consiste à traiter le HTML comme un composant
  • Il faut identifier la nature essentielle de ce composant
    • comment l’utilisateur obtient des informations supplémentaires sur un élément donné
    • lorsque l’utilisateur clique sur le bouton, soit l’information apparaît, soit une erreur est affichée faute d’information ; dans les deux cas, le composant revient à un état statique
  • Séparer les composants selon l’état

    • État placeholder :
      <!-- info-component-placeholder.html -->  
      <div id="info-component">  
          <button data-on-click="@get('/product/{{product.id}}/info')">  
              Get Info!  
          </button>  
      </div>  
      
    • État d’affichage de l’information :
      <!-- info-component-get.html -->  
      <div id="info-component">  
          {% if alert %}<div id="alert">{{ alert }}</div>{% endif %}  
          <p>{{product.additional_information}}</p>  
      </div>  
      
    • Quand le serveur rend le HTML, Datastar met automatiquement la page à jour
    • Penser au niveau du composant permet d’éviter d’entrer dans un état incorrect ou de perdre l’état utilisateur

Mettre à jour plusieurs composants en même temps

  • Ce qui avait marqué l’auteur dans la présentation de David Guillot, c’est que lorsque l’application mettait à jour le nombre d’éléments favoris, elle mettait aussi à jour un compteur situé très loin du composant modifié
    • Avec HTMX, cela déclenche un événement JavaScript, qui déclenche ensuite une requête GET vers le composant distant
  • Avec Datastar, plusieurs composants peuvent être mis à jour simultanément, y compris dans une fonction synchrone
  • Exemple de panier d’achat

    • Composant d’ajout au panier :
      <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>Quantity  
            <input name=quantity type=number data-bind-quantity value=1>  
          </label>  
          <button data-on-click="$quantity += 1">+</button>  
        </fieldset>  
        <button type=submit>Add to cart</button>  
        {% if msg %}  
          <p class=message>{{msg}}</p>  
        {% endif %}  
      </form>  
      
    • Composant d’affichage du compteur du panier :
      <div id="cart-count">  
          <svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">  
              <use href="#shoppingCart">  
          </svg>  
          {{count}}  
      </div>  
      
    • Dans Django, les deux composants sont mis à jour par la même requête :
      from datastar_py.consts import ElementPatchMode  
      from datastar_py.django import (  
          DatastarResponse,  
          ServerSentEventGenerator as SSE,  
      )  
      
      def add_item(request):  
          # 중요한 상태 업데이트 생략  
          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))  
              ),  
          ])  
      

Philosophie web native

  • Grâce à la communauté Discord de Datastar, l’auteur a compris que Datastar n’est pas juste un simple script utilitaire, mais une philosophie de construction d’applications fondée sur les primitives du web
  • Là où HTMX cherche à faire évoluer la spécification HTML, Datastar s’intéresse davantage à favoriser l’adoption des fonctionnalités natives du web
    • CSS view transitions
    • Server-Sent Events
    • Web Components, etc.
  • Un grand progrès a été d’extraire de simples Web Components réutilisables à partir de composants AlpineJS complexes après refactoring
  • C’est un excellent pattern pour obtenir une forte locality of behavior et de la réutilisabilité via la création d’éléments HTML personnalisés, sans outil comme React

Mises à jour en temps réel pour les applications multi-utilisateurs

  • Les applications qui intègrent la collaboration comme fonctionnalité de premier plan se distinguent des autres, et Datastar répond à ce besoin
  • La plupart des développeurs HTMX récupèrent les informations du serveur par polling, ou écrivent du code WebSocket personnalisé, ce qui augmente la complexité
  • Datastar utilise une technologie web simple, les Server-Sent Events (SSE), pour permettre au serveur de « pousser » des mises à jour vers les clients connectés
    • Quand un utilisateur ajoute un commentaire ou qu’un état change, le serveur met immédiatement à jour le navigateur, avec un minimum de code supplémentaire
    • Il devient possible de créer des tableaux de bord temps réel, des panneaux d’administration et des outils collaboratifs sans JavaScript personnalisé
  • Si la connexion côté client est interrompue, le navigateur tente automatiquement de se reconnecter, sans code additionnel
    • Le navigateur peut aussi indiquer au serveur le « dernier événement reçu »

Éviter la complexité excessive

  • La communauté Discord de Datastar aide à comprendre la vision de Datastar pour la création d’applications web
    • mises à jour d’UI pilotées par push
    • réduction de la complexité
    • traitement des cas localement complexes à l’aide d’outils comme les Web Components
  • La communauté aide aussi les nouveaux venus à réaliser qu’ils abordent parfois les choses de manière inutilement compliquée

Conseils clés

  • N’ayez pas peur de réafficher et renvoyer le composant entier
    • c’est plus simple et cela n’affecte pas beaucoup les performances
    • on peut obtenir une meilleure compression, et le navigateur parse les chaînes HTML très rapidement
  • Le serveur est la source de vérité et il est plus puissant que le navigateur
    • il vaut mieux laisser le serveur gérer la majeure partie de l’état, et vous n’aurez peut-être pas besoin d’autant de signaux réactifs que vous l’imaginez
  • Les Web Components sont excellents pour encapsuler la logique dans des éléments personnalisés avec une forte locality of behavior
    • l’animation du champ d’étoiles dans l’en-tête du site web de Datastar en est un bon exemple
    • l’élément <ds-starfield> encapsule tout le code de l’animation et expose trois attributs permettant de modifier son état interne
    • Datastar pilote ces attributs lorsque l’entrée range change ou que la souris passe au-dessus de l’élément

Des possibilités qui repoussent les limites

  • Ce qui est le plus enthousiasmant, c’est le potentiel que Datastar rend possible
  • La communauté produit régulièrement des projets qui vont bien au-delà des limites rencontrées par les développeurs utilisant d’autres outils

Cas remarquables

  • La démo de monitoring de base de données sur la page d’exemples
    • En exploitant l’hypermédia, elle améliore fortement la vitesse et l’usage mémoire par rapport à une démo présentée lors d’une conférence JavaScript
  • Les 10 milliards de cases à cocher d’Anders Murphy
    • Quand une expérimentation avec 1 million de cases à cocher a dépassé la capacité du serveur, Datastar a permis d’en implémenter 10 milliards sur un serveur peu coûteux
  • Une application web affichant les données de toutes les stations radar des États-Unis
    • Quand le signal d’un radar change, le point correspondant dans l’UI est modifié en moins de 100 millisecondes
    • Plus de 800 000 points sont mis à jour par seconde, et l’utilisateur peut remonter jusqu’à 1 heure en arrière avec un délai inférieur à 700 millisecondes
    • Le fait que cela soit possible dans une application hypermédia montre ce que Datastar peut rendre possible

Expérience d’utilisation actuelle

  • L’auteur est encore en phase d’exploration de Datastar et implémente rapidement et facilement les mises à jour d’UI par AJAX correspondant aux fonctions standard de HTMX
  • Il apprend et expérimente différents patterns pour accomplir davantage avec Datastar
  • Il s’intéresse depuis des décennies aux moyens d’offrir une meilleure expérience utilisateur grâce aux mises à jour en temps réel, et apprécie le fait que Datastar permette des mises à jour pilotées par push même dans du code synchrone
  • Il avait ressenti une grande joie en commençant à utiliser HTMX, mais depuis son passage à Datastar, il a le sentiment de n’avoir rien perdu et, au contraire, d’avoir énormément gagné
  • Si HTMX vous a procuré cette joie, vous ressentirez probablement le même bond avec Datastar, comme si vous redécouvriez ce que le web était censé faire depuis le début

2 commentaires

 
GN⁺ 2025-10-11
Commentaires sur Hacker News
  • J’apprécie que Chris sorte de sa zone de confort, relève un défi et partage cette expérience avec nous. Je suis peut-être un peu biaisé, puisque je construis des web apps avec htmx depuis quatre ans, mais je pense que cela met bien en lumière la principale différence d’architecture entre Datastar et htmx : htmx est orienté HTML, Datastar est orienté serveur. Il est vrai que l’API côté client est plus simple, mais c’est parce que la logique côté serveur devient plus complexe. Par exemple, si un élément HTML n’a pas d’information sur l’endroit où insérer un fragment renvoyé par le serveur, alors cette information doit être conservée côté serveur ; la complexité doit forcément exister quelque part. Le choix d’architecture semble surtout relever des préférences. Dans l’exemple, l’argument du « less attributes » n’est pas totalement équitable, car il cite aussi des attributs optionnels dans htmx ; par exemple, retirer hx-trigger="click" réduit déjà le nombre d’attributs de 20 %. Et la démonstration gagnerait en crédibilité avec un HTML plus accessible, par exemple en utilisant <button> au lieu de <span>. Au final, le point fort de Datastar semble être d’intégrer nativement des fonctionnalités qu’on trouve dans Alpine ou Stimulus, et ça, c’est vraiment impressionnant
    • Avec Datastar, j’ai l’impression que la complexité diminue fortement, car on peut tout renvoyer et tout rafraîchir d’un coup, sans avoir à implémenter séparément un système d’événements pour mettre à jour en temps réel d’autres parties de la page. Bien sûr, selon les cas, une approche orientée événements ou du chargement différé peut être préférable
    • En lisant l’idée que « c’est comme si les fonctionnalités d’Alpine ou Stimulus étaient intégrées par défaut à HTMX », je me demande si je ne devrais pas utiliser HTMX pour un projet perso. Je me demande s’il existe des ressources qui expliquent qu’il faut aussi ajouter des bibliothèques comme AlpineJS ou Stimulus
    • Il y avait cette discussion disant que, si l’élément HTML ne contient pas l’information sur l’endroit où insérer le fragment, le serveur doit la connaître ; mais je me demande si, dans ce cas, le front-end ne serait pas plus léger et plus rapide. Surtout s’il y a énormément d’éléments, non ?
    • Cette structure ressemble un peu au framework Seaside de Pharo. Quand notre entreprise développait une app B2B en Pharo, l’état de l’UI était géré dans le backend, donc il y avait beaucoup d’allers-retours entre le front et le backend. Pour de la B2B où le temps réel et la latence ne sont pas critiques, ça va, mais ce n’est pas adapté à des apps B2C à grande échelle
  • Pour avoir utilisé directement Datastar et HTMX, je ne vois toujours pas très bien quelle grande différence cela ferait de construire une app avec Datastar. J’utilise FastAPI, HTMX, Alpine.js et SSE ensemble pour afficher des logs en temps réel, mettre à jour l’état des déploiements, etc. En regardant les exemples Datastar, je ne vois pas clairement ce qui serait plus simple que cette architecture. (Référence du code : devpush SSE partial). J’avais aussi essayé les Web Components à l’époque du développement de Basecoat, mais à cause de problèmes de style, de gestion d’état et d’autres raisons, je suis finalement revenu au HTML/CSS/JS traditionnel. devpu.sh, basecoatui.com
    • Même avec HTMX, lorsqu’on réfléchit à l’évolutivité fonctionnelle comme dans Datastar, il est souvent plus simple de rafraîchir toute la liste d’un coup. Au lieu de vouloir mettre à jour l’état de chaque déploiement individuellement, mettre à jour toute la liste est bien plus simple et produit un code plus léger, car on n’a plus à se soucier des cas limites comme la pagination
  • Pour ceux qui pensent que Datastar n’est pas suffisant pour le temps réel, la collaboration ou le multijoueur, je voudrais présenter trois démos passées en page d’accueil de HN qui fonctionnent sans fonctionnalités PRO, et même sur un VPS à 5 dollars. Cela montre à quel point Datastar est une technologie bien conçue : Checkboxes, Cells, Game of Life Example. Les exemples Checkboxes et Cells ont un rendu de vue dynamique, ce qui permet de beaucoup dézoomer, et ils disposent aussi de backpressure pour le défilement virtuel
    • Si j’ai bien compris la structure du code, il semble qu’en pratique ils n’utilisent pas la méthode de patch recommandée par datastar (diff/patch) et rerendent toute la page à chaque fois. En fait, ce modèle mental me paraît bien plus simple que des exemples où le serveur suit finement l’état du client, et c’est justement ce qui me plaît. Je me demande si des apps complexes classiques pourraient aussi être construites ainsi. Si quelqu’un a un texte de référence sur la façon de gérer le rerender immédiat tout en suivant l’état de divers widgets quand les utilisateurs naviguent entre différentes pages, je serais preneur
    • Dans les exemples Checkboxes/Cells, vous dites qu’on peut « dézoomer », mais je me demande concrètement comment on fait. Et j’aurais trouvé encore mieux s’il y avait une option du type data-replace-url qui mette automatiquement à jour l’URL de la vue courante avec ces coordonnées (x=123&y=456, etc.)
    • En voyant la mention des fonctionnalités PRO, j’ai compris qu’il s’agissait d’un modèle open core (une partie open source, le reste payant, licence à 299 dollars). Je préfère passer mon tour
  • J’ai récemment lu ce billet (htmx, datastar, greedy developer), et j’ai entendu dire que certaines bonnes fonctionnalités de base de Datastar avaient été déplacées vers l’offre payante (Pro). Je suis prêt à soutenir financièrement un framework, qu’il soit open source ou payant, mais ce genre de précédent m’inquiète un peu
    • Moi aussi, je suivais Datastar depuis quelques mois en attendant la sortie de la 1.0.0, mais maintenant tout mon enthousiasme est retombé. J’ai trop souvent été déçu par des projets « open source, mais pas vraiment », donc j’ai du mal à faire confiance
    • À vrai dire, j’avais déjà écrit que je n’aimais pas trop Datastar, mais cette fois j’ai envie de le défendre un peu. Le créateur du framework a publié gratuitement son code sous licence MIT, donc les fonctionnalités auparavant offertes gratuitement restent toujours utilisables sous licence MIT. Si on s’est contenté de l’utiliser sans contribuer, dépendre des anciennes versions relève de son propre choix. À partir de maintenant, passer à un modèle payant est la liberté du propriétaire du produit, et si nécessaire, on peut simplement faire un fork
    • J’ai payé une fois la licence PRO à 299 dollars, mais je n’ai encore jamais réellement utilisé les fonctionnalités PRO. Je voulais créer un clone de Google Sheets, mais c’était tout à fait faisable même sans PRO. (voir la démo cells)
    • J’ai parfois trouvé que l’équipe Datastar dépassait le simple fait de dire à quel point Datastar est bien sur le Discord HTMX, et devenait un peu agressive. J’ai aussi vu sur Reddit un commentaire du genre : « si la version bêta perpétuelle vous suffit parce qu’elle a déjà tout ce qu’il vous faut, utilisez-la ; l’open source ne doit rien à personne »
    • L’ancien cas de Meteor.js m’est immédiatement revenu en tête en utilisant Datastar. (discussion HN sur Meteor.js)
  • Je ne comprends pas très bien les exemples de code montrés dans ce post. Par exemple, l’exemple htmx suivant <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> semble devenir ce code datastar : <span data-on-click="@get('/rebuild/status-button')"></span> Et les autres exemples sont encore plus déroutants. Au final, je ne comprends pas pourquoi on passerait de htmx à Datastar
    • Fondamentalement, HTMX signifie : « quand on clique sur ce span, récupérer du HTML depuis /rebuild/status-button, extraire l’élément #rebuild-bundle-status-button du HTML renvoyé, puis remplacer l’élément existant ». À l’inverse, Datastar signifie : « au clic sur le span, suivre simplement les instructions de /rebuild/status-button ». Si le serveur renvoie plusieurs éléments avec des ID, Datastar les reconnaît tous automatiquement et les remplace. Autrement dit, il suffit des ID pour obtenir le comportement voulu, sans avoir à utiliser target, select ou swap
    • L’architecture de Datastar concentre la logique dans le backend. Comme avec le HTML traditionnel, on fait une requête, on reçoit du HTML et le navigateur le rend immédiatement, mais avec Datastar, une fois la page chargée, chaque interaction envoie une requête au backend et n’applique que les changements reçus. C’est en quelque sorte l’inverse d’une SPA, où le front n’a pas de logique et où le backend gère tout l’état. Au fond, c’est encore une redistribution de la logique entre backend et front-end ; Datastar permet de concentrer cette logique côté serveur tout en conservant une interface dynamique
    • Mais je me demande pourquoi utiliser une balise span pour quelque chose de cliquable. Un button ou un lien semblerait plus approprié
  • Ironiquement, alors que l’hypermédia est le sujet de cet article, il ne contient même pas de lien vers le site officiel de Datastar. Le voici : https://data-star.dev/
  • L’un des grands avantages de HTMX est que le client n’a pas besoin de connaître la structure des données renvoyées par le serveur ; mais si le client doit connaître jusqu’aux ID ou à la signification des éléments individuels, cette promesse semble rompue. Bien sûr, dans la pratique, beaucoup de projets utilisent l’OOB (Out of Band), donc une séparation de structure parfaitement pure est aussi assez éloignée de la réalité. Ce serait bien de trouver une manière de prendre le meilleur des deux mondes
    • En pratique, le client n’a besoin de ne rien savoir du tout. Le client peut exécuter une action, et le serveur peut alors renvoyer la vue complète de la page. Le client la rend immédiatement. C’est proche du modèle en immediate mode des jeux vidéo
  • Une structure comme celle de Datastar, où le serveur patche le HTML ainsi, ne me paraît pas très bonne du point de vue de la séparation des responsabilités, et plus l’application grossit, plus injecter en permanence des morceaux de HTML depuis le serveur me semble pénible à maintenir
    • Cela dit, laisser du JS injecter du HTML fragmenté un peu partout n’est pas forcément mieux
    • Cette manière de concevoir des endpoints qui diffusent des fragments HTML me met vaguement mal à l’aise
  • Datastar me paraît plus abouti que htmx. J’ai mené plusieurs projets à bien avec htmx, mais j’ai toujours regretté de devoir ajouter du glue code JavaScript pour la gestion des événements, surtout avec AlpineJS. Si Datastar peut réduire ce besoin aussi, c’est vraiment prometteur
    • Je recommande de lire l’essai grugs around the fire sur le site de Datastar
  • Je suis arrivé assez tard à la tendance hypermédia, mais j’ai d’abord utilisé Datastar avant de passer récemment à HTMX. L’API de Datastar est un peu meilleure, mais depuis que htmx 2.0 prend en charge les mises à jour OOB (Out-Of-Band), je penche surtout du côté de htmx
    • L’expression « arrivé tard à l’hypermédia » m’a marqué. Il est intéressant de rappeler que Ted Nelson a forgé ce terme en 1965, et qu’il écrivait alors : « hyperfilm— a browsable or vari-sequenced movie— is only one of the possible hypermedia that require our attention ». Voir l’article
    • Ce qui me gêne dans la gestion des éléments OOB dans HTMX : 1. s’il s’agit d’OOB, htmx-swap-oob="true" est obligatoire ; sans cet attribut, cela ne fonctionne pas comme attendu 2. inversement, si ce n’est pas de l’OOB, la présence de htmx-swap-oob="true" le fait être ignoré ou mal fonctionner. Résultat, pour réutiliser le même composant en version OOB ou non OOB, il faut que le serveur renvoie à chaque fois un flag isOob, ce qui est vraiment pénible
    • Je préfère l’API de alpine-ajax. Il suffit de spécifier plusieurs cibles, et les éléments sont remplacés de manière cohérente sans JS. Les notions de signal/state de Datastar me donnent au contraire l’impression d’ajouter de la complexité inutilement