Accueil / Articles / Mise à niveau vers htmx 4 : Fetch, héritage explicite et ce qui cesse de fonctionner

Mise à niveau vers htmx 4 : Fetch, héritage explicite et ce qui cesse de fonctionner

Comprendre les changements architecturaux d’HTMX 4, allant du noyau basé sur Fetch et de l’héritage explicite des attributs aux échanges d’erreurs, au morphing et à l’historique, afin de planifier une migration sécurisée.

2717 mots

Htmx repose sur une hypothèse simple et délibérément désuète : le serveur renvoie du HTML, le navigateur insère ce HTML dans la page, et une application fonctionnelle se crée sans reproduire l’ensemble de l’interface en tant qu’état côté client. La version 4 conserve cette hypothèse tout en reconstruisant le mécanisme sous-jacent à partir des primitives modernes des navigateurs. Ce guide explique ce qui a changé et pourquoi, quels changements peuvent endommager silencieusement une application existante, et comment effectuer la mise à niveau comme un audit structuré plutôt qu’un simple passage de version. Les détails correspondent à htmx 4 telle que documentée au moment de la rédaction ; vérifiez les spécificités auprès des notes de version actuelles avant de migrer.

Le contrat préservé par htmx 4

C’est utile de se rappeler ce que remplace Htmx. Une application typique rendue côté client demande des données JSON à une API, conserve ces données en JavaScript, affiche les composants à partir d’elles et met constamment en concordance l’état local avec celui du serveur. Htmx élimine la majeure partie de cette couche intermédiaire. Le serveur répond avec la représentation dont l’utilisateur a réellement besoin, à savoir du HTML.

Prenons un bouton annoté avec des attributs tels que hx-post, une cible comme #task-list, ainsi qu’une stratégie d’ajout. Lorsqu’on clique dessus, Htmx collecte le contexte de la demande, l’envoie au serveur, analyse le markup reçu et ajoute ce markup à la liste des tâches. La validation, l’autorisation, la persistance et la présentation restent du ressort du serveur. L’interaction, la navigation, le focus ainsi que le document lui-même restent gérés par le navigateur.

C’est bien plus qu’une manière concise de désigner fetch(). Ces attributs décrivent un contrôle hypermédia : quelle action est disponible, où elle est envoyée, et comment la représentation résultante doit être intégrée à la page actuelle. Le fragment retourné peut lui-même contenir des liens et des formulaires indiquant les prochaines actions possibles, exactement comme une page complète. En d’autres termes, HTML reste le protocole d’application ; htmx se contente de coordonner l’échange entre les deux parties.

La version 4 maintient cette interface publique de manière presque monotone et stable. Ce qu’elle réorganise, c’est le cycle de vie qui se trouve en dessous. Le résultat n’est pas un nouveau framework front-end, mais plutôt des règles plus strictes régissant la coopération entre HTML, HTTP, le DOM et le serveur gérant l’état.

Un noyau reconstruit sur l’API Fetch

Les versions précédentes utilisaient XMLHttpRequest. Cela était logique lorsque htmx devait fonctionner sur des navigateurs plus anciens, et XHR fournissait des événements de progression du téléchargement dont certaines applications avaient besoin. Cependant, avec le temps, cette décision de compatibilité s’est transformée en dette architecturale. L’équipe htmx a réécrit la logique de demande en utilisant l’API Fetch basée sur les promesses, s’appuyant sur des expérimentations menées avec un projet plus petit appelé Fixi ainsi que sur le HTML en flux continu.

Un schéma de nommage des événements prévisible

Cette approche plus claire se reflète dans le modèle des événements. Les noms d’événement suivent désormais un schéma cohérent, htmx:phase:action, pouvant être complétés éventuellement par une sous-action (htmx:phase:action:sub-action). Comme la phase figure en premier, il est possible de savoir d’un coup d’œil si un écouteur s’exécute avant ou après l’étape qu’il correspond.

Chaque événement lié à une requête reçoit également le même objet de contexte. Les extensions et les écouteurs n’ont plus besoin de rassembler divers détails spécifiques à l’événement pour trouver l’élément source, la configuration de la requête, la réponse et le échange en attente ; tout se trouve en un seul endroit. Les requêtes disposent en outre d’une phase finally qui s’exécute quel que soit le résultat : succès, échec ou annulation. C’est l’endroit idéal pour effectuer des tâches de nettoyage telles que cacher les indicateurs de chargement ou réactiver les boutons.

Si votre base de code écoute les événements htmx par nom, chaque écouteur doit être revu, car anciens noms ne correspondent pas au nouveau schéma.

Wrapper abandonnés au profit de la plateforme

Htmx 4 supprime également les fonctions d’aide qui dupliquaient des API que les navigateurs fournissent désormais de manière fiable :

  • htmx.addClass() est remplacé par element.classList.add()
  • htmx.closest() a été remplacé par element.closest()
  • htmx.remove() a été remplacé par element.remove()
  • C’est une évolution positive. Une petite bibliothèque ne devrait pas conserver indéfiniment des API pratiques une fois que la plateforme les a intégrées, et les remplacements sont des appels DOM standard que tout développeur connaît déjà.

    L’héritage des attributs est désormais optionnel

    Le changement de migration le plus significatif n’a rien à voir avec Fetch. Htmx 4 cesse d’hériter implicitement de la plupart des attributs des éléments ancêtres.

    Auparavant, un attribut placé sur un conteneur, par exemple une cible, une demande de confirmation ou un ensemble d’en-têtes de requête, s’appliquait automatiquement à tous les descendants gérés par htmx. Dans la version 4, vous devez déclarer explicitement cette portée en ajoutant le suffixe :inherited au nom de l’attribut sur le parent. Un descendant qui souhaite étendre une valeur ou un sélecteur hérité, plutôt que de le remplacer, peut utiliser le suffixe :append.

    Ces suffixes ne sont pas décoratifs. Ils indiquent à quiconque lit le modèle que l’attribut du parent fait intentionnellement partie du comportement de ses enfants. Cela rapproche encore davantage htmx de la localisation du comportement : plus une déclaration se trouve près de l’élément qu’elle concerne, moins le lecteur doit reconstituer un contexte invisible. Un comportement partagé reste possible ; son champ d’application est simplement indiqué là où il est déclaré.

    Pourquoi c’est la partie la plus risquée de la mise à niveau

    Cette modification crée également un mode de défaillance silencieux. Supposons qu’un en-tête CSRF ait toujours été défini dans un conteneur de mise en page. Après la mise à niveau, la page s’affiche exactement comme avant, mais les requêtes provenant des éléments enfants ne contiennent plus cet en-tête et commencent à être rejetées par le serveur. Rien ne semble anormal tant que quelqu’un n’envoie pas un formulaire.

    Htmx propose un vérificateur de mise à niveau officiel qui analyse les modèles et les scripts en quête d’héritage implicite, de noms d’événements obsolètes, d’attributs supprimés et d’API dépassées. Utilisez son rapport comme liste de départ, mais pas comme garantie, puis testez les vrais chemins de requête, en particulier ceux protégés par des en-têtes, des confirmations ou des cibles partagées.

    Les réponses d’erreur deviennent des fragments interchangeables

    Dans Htmx 2, les réponses ayant des codes d’état 4xx ou 5xx n’étaient pas remplacées par défaut. Htmx 4 inverse cette logique : il remplace toutes les réponses HTTP à l’exception de 204 No Content et 304 Not Modified.

    Lorsque différentes familles de codes d’état doivent être affichées en des endroits différents ou suivre des règles de remplacement distinctes, le nouveau attribut hx-status vous permet de configurer cela pour chaque classe d’état, par exemple en envoyant les erreurs de validation dans une zone de message intégrée tandis que les erreurs serveur sont affichées dans un bandeau au niveau de la page.

    La véritable conséquence se fait sentir sur le serveur. Chaque réponse d’erreur doit désormais être un fragment valide pour l’élément qui la recevra. Un historique complet d’erreurs ou un corps d’erreur JSON brut sera inséré dans le DOM tel quel. Les codes d’état conservent leur signification HTTP pour les caches, les journaux et les clients, tandis que le corps HTML contient l’affichage et, idéalement, la prochaine action que l’utilisateur peut entreprendre, comme un formulaire corrigé.

    Mettre à jour plusieurs régions à partir d’une seule réponse

    Une seule opération côté serveur nécessite souvent de modifier plus que simplement l’élément sur lequel l’utilisateur a cliqué. Envoyer un message peut par exemple l’ajouter à une chronologie, augmenter le compteur des messages non lus et remplacer le contrôle de pagination. Htmx propose depuis longtemps des mécanismes de remplacement hors bande pour cela : les éléments indiqués comme tels dans la réponse remplacent les éléments correspondants ailleurs dans le document.

    Htmx 4 intègre un outil plus explicite : l’élément <hx-partial>. Chaque partie déclare son propre cible et sa stratégie de remplacement, ce qui permet à la réponse d’apparaître comme une liste d’actualisations clairement identifiées.

    L’ordre de traitement est également défini. La réponse principale est remplacée en premier ; les parties et les éléments hors bande suivent dans l’ordre du document. Cela encourage à rendre chaque mise à jour significative en soi, plutôt que de dépendre d’effets secondaires liés à un changement précédent dans le DOM au sein de la même réponse.

    Cela revient à une orchestration légère des réponses. Le serveur peut décrire toutes les conséquences visibles d’une opération dans une seule réponse, sans avoir à retourner du JSON et de laisser le code client gérer la répartition des champs entre les composants.

    Mutations de remplacement qui maintiennent l’état du navigateur

    Remplacer innerHTML est facile à comprendre, mais cela élimine l’état géré par le navigateur. Un champ de texte peut perdre sa sélection, le focus peut être déplacé, une vidéo peut se relancer, et un élément personnalisé peut être détruit puis recréé même si la majeure partie de son markup reste inchangée.

    Htmx 4 intègre les modes de remplacement innerMorph et outerMorph, basés sur une version améliorée de l’algorithme Idiomorph. Au lieu d’éliminer le sous-arbre cible, ce mécanisme compare les nœuds anciens et nouveaux et applique l’ensemble de modifications le plus minimal possible. Les nœuds qui correspondent conservent leur identité, ainsi que leur focus, leur sélection, leur position de lecture et leur état interne.

    Le morphing n’est pas automatiquement le meilleur choix. Pour un fragment simple, un remplacement complet est souvent plus sûr et plus facile à déboguer. Le morphing s’avère utile lorsque la cible contient des contrôles de formulaire en temps réel, des éléments personnalisés, du média ou des widgets tiers dont l’identité est importante. Htmx propose également des sélecteurs permettant de sauter des nœuds entiers, ou seulement leurs enfants, lors d’un morphing, ce qui constitue un moyen pratique de protéger des éléments à état comme une carte intégrée ou un éditeur de texte enrichi.

    Le principe général vaut la peine d’être retenu même en dehors d’htmx : le serveur décide de la forme que doit avoir l’HTML, et le navigateur conserve les objets DOM physiques qui doivent survivre à la transition.

    Le streaming se trouve dans les extensions, pas dans le noyau

    Fetch offre à htmx une base bien meilleure pour les réponses en flux, mais le noyau ne force pas l’utilisation d’un protocole de streaming unique pour tout le monde. Au lieu de cela, htmx 4 intègre des extensions distinctes et ciblées pour les Server-Sent Events, WebSockets et les réponses multipart.

    Celle destinée aux données multipart, hx-multipart, prend en charge les corps de type multipart/mixed et multipart/parallel. Chaque partie peut contenir du HTML ainsi que ses propres en-têtes d’action HX-*. Cela permet à un serveur d’envoyer immédiatement un élément de remplacement, puis de diffuser des sections supplémentaires au fur et à mesure que le traitement lent se termine, en ciblant chacune d’elles individuellement, tout cela sans avoir besoin d’un bus de messages côté client construit manuellement.

    Le choix parmi ces options dépend de la structure du flux de données :

    • SSE convient aux mises à jour ordonnées et unidirectionnelles envoyées depuis le serveur.
    • WebSockets conviennent aux communications véritablement bidirectionnelles.
  • Multipart convient à une seule opération HTTP qui produit plusieurs représentations au fil du temps.
  • Tous les trois s’intègrent dans le même mécanisme d’échange, de sorte que le reste de votre balisage n’a pas besoin de savoir quel transport a livré un fragment.

    Un modèle d’extension plus simple

    En gardant le streaming en dehors du noyau, celui-ci reste compact, tandis que les extensions gagnent en capacités. Les extensions d’htmx 4 s’enregistrent directement et peuvent interagir avec les phases de demande, de réponse et d’échange. Vous activez simplement une extension en incluant son script ; l’attribut hx-ext n’existe plus. Si vous souhaitez une frontière stricte, la configuration vous permet de restreindre les noms d’extensions autorisés sur un site.

    HCON : une notation compacte pour les options d’attribut

    Au fur et à mesure que les attributs accumulaient des options, htmx avait besoin d’une syntaxe moins encombrée que le JSON intégré dans la valeur d’un attribut. La solution est HCON, la notation d’objets de configuration htmx. Elle prend en charge des paires clé-valeur séparées par des espaces, des flags sous forme de booléens, des nombres, des chaînes entre guillemets ainsi que des clés avec points pour le nesting.

    Le JSON reste également accepté, ce qui est pratique lorsque un serveur génère déjà la configuration. HCON vise les marquages manuscrits : suffisamment concis pour être consultés rapidement, mais suffisamment structurés pour que htmx n’ait pas besoin d’un analyseur ad hoc distinct pour chaque attribut. La même notation est utilisée pour les déclencheurs, les modificateurs de swap, la configuration des requêtes, les en-têtes, les valeurs ainsi que l’en-tête de réponse HX-Location ; donc en apprendre une fois permet d’en tirer profit partout.

    hx-live pour l’état client restant

    L’hypermédia ne supprime pas toute interactivité locale. Un menu déroulant s’ouvre avant même que toute demande ne soit faite. Un compteur de caractères se met à jour à chaque frappe. Les onglets, les widgets d’affichage et les sélections temporaires relèvent généralement de la responsabilité du navigateur.

    La nouvelle extension hx-live couvre ces cas grâce à une fine couche de script axée sur le DOM. Elle propose un outil d’interrogation, des sélecteurs directionnels pour trouver des éléments proches, des utilitaires DOM, des outils asynchrones, un accès typé aux attributs et aux valeurs de données, ainsi que des liaisons réactives telles que :text, :class et :hidden.

    Sa règle fondamentale est de nature philosophique : le DOM constitue le stockage d’état. Une expression réactive lit l’état des éléments voisins et met à jour leur présentation correspondante. Tout ce qui est durable reste sous le contrôle du serveur et parvient à la page sous forme d’HTML, comme avant.

    Pensez à hx-live comme une soupape de décharge pour les petites tâches d’interface utilisateur, et non comme une invitation à développer une deuxième application dans le navigateur. Utilisez-le lorsque vous seriez sinon amené à écrire du code générique répétitif pour gérer des événements dans une interface temporaire. Lorsque l’état doit persister après un changement de page, être partagé entre utilisateurs, appliquer des permissions ou participer à des transactions, il doit rester sur le serveur.

    La navigation dans l’historique récupère de nouvelles données au lieu de restaurer des snapshots

    Htmx 2 stockait les snapshots de l’historique dans localStorage. Leur restauration pouvait ramener des mutations du DOM effectuées par des scripts indépendants, sans pour autant restituer l’état en temps de exécution du JavaScript qui les avait créées. La page semblait interactive, mais n’était en réalité qu’un DOM fossilisé : des widgets étaient affichés, sans que rien ne soit connecté derrière eux.

    Htmx 4 supprime ce cache par défaut. Lors de la navigation avant ou arrière, il récupère à nouveau la page et remplace son contenu dans <body> ou dans un élément d’historique spécifié. Des en-têtes de cache HTTP appropriés peuvent rendre cette requête presque gratuite tout en fournissant aux scripts un document propre à initialiser.

    Les applications qui ont réellement besoin de captures locales peuvent charger l’extension hx-history-cache, qui utilise sessionStorage et rend ce comportement explicite. Ce schéma se retrouve tout au long de la version : une représentation fraîche est la norme, tandis que la reconstruction locale constitue une fonctionnalité optionnelle dotée d’un nom propre.

    Considérez la migration comme un audit comportemental

    La mise à niveau la plus sûre n’est pas un simple remplacement de paquet aveugle, mais une brève analyse de tous les endroits où le comportement dépasse les limites du markup. La plupart des pages conservent leur markup inchangé ; le travail se concentre sur l’héritage implicite, les écouteurs d’événements, le traitement des réponses, l’historique et les extensions.

    Fixez d’abord une version exacte de htmx 4, puis exécutez le vérificateur officiel de mise à niveau décrit dans la documentation de migration. Traitez ses résultats dans un ordre qui évite les conflits entre attributs renommés :

    1. Renommez l’ancien hx-disable, qui signifiait « ignorer cette sous-arborescence », en hx-ignore.
    2. Seulement ensuite, renommez hx-disabled-elt en le nouveau hx-disable. Effectuer ces deux étapes dans l’ordre inverse transformerait par erreur un attribut en l’autre.
  • Ajoutez :inherited là où un parent doit continuer d’influencer ses descendants, en prêtant une attention particulière aux en-têtes, aux confirmations, aux cibles et aux inclusions.
  • Mettez à jour les noms des écouteurs d’événements et remplacez les outils supprimés par des API natives du navigateur.
  • Testez les réponses 4xx et 5xx, puisqu’elles sont désormais échangées par défaut, et assurez-vous que chacune renvoie un fragment pertinent.
  • Testez chaque élément hx-delete, qui ne transmet plus les données du formulaire contenant l’élément sauf si vous le demandez avec hx-include.
  • Testez la navigation avant/arrière, le tri hors bande, les délais d’expiration, les files d’attente des requêtes ainsi que chaque extension que vous utilisez, en gardant à l’esprit que hx-ext a disparu.
  • Lorsque htmx 4 est l’outil idéal

    Le changement majeur est le nom « Fetch », mais le thème unificateur reste l’explicitité. Un attribut n’est transmis aux éléments descendants que si sa déclaration le prévoit. Par défaut, l’HTML d’une requête échouée est affiché à l’utilisateur, et ce sont les exceptions qui doivent être configurées. Les réponses concernant plusieurs zones précisent où chaque élément se trouve et comment il est remplacé. La navigation avant/arrière charge une page vierge, sauf si vous activez délibérément un cache d’images nommé. Les extensions s’intègrent via un cycle de vie commun, et les méthodes DOM standard prennent le relais là où htmx utilisait auparavant ses propres outils.

    Tout cela permet de réduire les comportements cachés sans transférer l’état de l’application dans un framework client. C’est l’équilibre que vise htmx : des interactions riches, une autorité serveur, et un HTML qui décrit toujours ce que la page peut faire.

    Cela ne rendra pas toutes les interfaces plus simples. Un éditeur graphique, un espace de travail optimisé pour le fonctionnement hors ligne ou une application basée sur un modèle local fortement collaboratif peuvent justifier une architecture client plus riche. Cependant, de nombreuses applications métier se composent principalement de fonctionnalités de navigation, de formulaires, de tableaux, de validation et de flux de travail que le serveur gère déjà. Dans ce cas, fournir la représentation finale est souvent plus simple que de maintenir deux machines à états synchronisées.

    Points clés

    • Le modèle htmx reste inchangé : les éléments sont des contrôles hypermédias, les réponses sont en HTML, et le serveur gère l’état.
    • L’héritage implicite des attributs a disparu ; l’absence de suffixes :inherited est la cause la plus fréquente de dysfonctionnements silencieux, en particulier pour les en-têtes CSRF.
    • Les réponses d’erreur sont désormais échangées par défaut, de sorte que chaque corps de réponse 4xx et 5xx doit être un fragment valide pour son destinataire.
  • <hx-partial> ainsi que les échanges morphiques rendent explicites et prévisibles les mises à jour multi-régions et les échanges tout en préservant l’état.
  • Le streaming, l’interactivité côté client et le cache d’historique ont été déplacés dans des extensions optionnelles, permettant de garder le noyau léger.
  • Exécutez le vérificateur de mise à niveau, puis testez des chemins de requête réels ; le vérificateur identifie des candidats, mais seuls les tests permettent de confirmer que l’application fonctionne toujours.
  • Lectures complémentaires