Pourquoi je suis passé de HTMX à Datastar
(everydaysuperpowers.dev)- 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
- En 2022, David Guillot a présenté à DjangoCon Europe un cas où un SaaS basé sur React a été migré vers HTMX, réduisant le volume de code d’environ 70 % tout en améliorant les fonctionnalités
- Par la suite, de nombreuses équipes ont constaté qu’en passant d’une application monopage (SPA) à une application hypermédia multipage, elles réduisaient le code tout en améliorant à la fois l’expérience de développement et l’expérience utilisateur
- L’auteur a lui aussi migré un projet de HTMX vers Datastar, et a constaté que le code devenait plus court et qu’il était possible de développer une application temps réel multi-utilisateurs sans WebSocket ni gestion d’état complexe
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’IDinfo-detailsdans la réponse, et l’élément de la page portant l’IDalertest 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
- Quand on clique sur le bouton, une requête GET est envoyée à
-
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
- Le serveur renvoie une chaîne HTML contenant deux éléments racine portant les mêmes ID
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
- État placeholder :
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)) ), ])
- Composant d’ajout au panier :
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
Datastar - un framework hypermédia léger pour créer des applications web interactives
Commentaires sur Hacker News
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 impressionnantdiff/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 preneurdata-replace-urlqui mette automatiquement à jour l’URL de la vue courante avec ces coordonnées (x=123&y=456, etc.)<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/rebuild/status-button, extraire l’élément#rebuild-bundle-status-buttondu 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 à utilisertarget,selectouswapspanpour quelque chose de cliquable. Unbuttonou un lien semblerait plus approprié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 dehtmx-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 flagisOob, ce qui est vraiment pénible