⚡ HTMX en pratique

HTMX permet d'ajouter de l'interactivité (requêtes AJAX, mises à jour partielles du DOM, validation, polling…) directement via des attributs HTML, sans écrire de JavaScript. Chaque bloc ci-dessous est une démo fonctionnelle avec son explication et son code juste à côté.

1. Requête GET simple hx-get

Le clic sur le bouton déclenche une requête GET vers le serveur. La réponse HTML renvoyée remplace le contenu de l'élément ciblé par hx-target (ici #hello-result), grâce à hx-swap="innerHTML" (valeur par défaut). Un indicateur de chargement (hx-indicator) s'affiche pendant la requête.

<button hx-get="index.php?action=hello"
        hx-target="#hello-result"
        hx-indicator="#hello-spinner">
  Dire bonjour au serveur
</button>
Démo live
Aucune requête envoyée pour le moment.

2. Recherche en direct hx-trigger + delay

À chaque frappe (keyup), une requête est envoyée après 500ms sans nouvelle frappe (delay:500ms) — ça évite de spammer le serveur. hx-trigger="keyup changed delay:500ms" combine trois conditions. Le contenu de l'input est envoyé automatiquement comme paramètre q car c'est un champ <input> dans le périmètre de la requête.

<input type="text" name="q"
       hx-get="index.php?action=search"
       hx-trigger="keyup changed delay:500ms"
       hx-target="#search-results">
Démo live — essaie "an", "poire", "z"…
Tape quelque chose pour chercher…

3. Validation en temps réel hx-get sur un champ

Le champ email valide côté serveur (PHP) à chaque changement de focus (blur), sans recharger la page ni soumettre tout le formulaire. Utile pour de la validation "vraie" (unicité en base, format métier…) impossible à faire en JS pur.

<input type="email" name="email"
       hx-get="index.php?action=validate-email"
       hx-trigger="blur, keyup changed delay:400ms"
       hx-target="#email-hint">
<span id="email-hint"></span>
Démo live

4. Ajouter / supprimer des éléments hx-swap="beforeend" / outerHTML

"Ajouter" fait un POST et insère la réponse à la fin de la liste (hx-swap="beforeend") sans toucher au reste. "Supprimer" fait un DELETE sur l'élément lui-même et le remplace par une réponse vide via hx-swap="outerHTML", ce qui le fait disparaître du DOM.

<button hx-post="index.php?action=add-item"
        hx-target="#item-list"
        hx-swap="beforeend">+ Ajouter</button>

<li>Élément
  <button hx-delete="index.php?action=delete-item"
          hx-target="closest li"
          hx-swap="outerHTML">✕</button>
</li>
Démo live
  • Élément initial

5. Confirmation avant envoi hx-confirm

hx-confirm affiche une boîte de dialogue native avant d'envoyer la requête. Si l'utilisateur clique "Annuler", aucune requête n'est envoyée.

<button hx-delete="index.php?action=confirmed-delete"
        hx-confirm="Vraiment supprimer ?"
        hx-target="#confirm-result">
  Supprimer le compte
</button>
Démo live

6. Polling automatique hx-trigger="every 3s"

L'élément relance automatiquement une requête toutes les 3 secondes tant qu'il est dans le DOM — pratique pour un statut de job, une barre de progression, un chat… Aucune boucle JS à gérer.

<div hx-get="index.php?action=poll-status"
     hx-trigger="every 3s">
  ...
</div>
Démo live (se met à jour toute seule)
Chargement…

7. Onglets chargés à la demande hx-get + hx-target partagé

Chaque bouton charge le contenu de son onglet uniquement au clic (pas tout chargé d'avance). Tous ciblent la même zone #tab-content. La classe active est gérée par HTMX via hx-on::after-request pour illustrer qu'on peut quand même ajouter un peu de logique JS ciblée si besoin.

<button hx-get="index.php?action=tab&name=a"
        hx-target="#tab-content">Onglet A</button>
Démo live

Contenu de l'Onglet A. Chargé via hx-get au clic.

🚀 Cas avancés

8. Mise à jour "hors bande" hx-swap-oob

Une seule requête/réponse peut mettre à jour plusieurs zones différentes de la page. Le bouton cible la liste normalement, mais la réponse du serveur contient en plus un fragment marqué hx-swap-oob="true" qui va se placer tout seul dans l'élément portant le même id ailleurs sur la page (ici, le compteur en haut) — sans passer par hx-target.

<!-- réponse serveur pour un seul clic -->
<li>Élément #4 <button hx-delete="...">✕</button></li>

<span id="oob-counter" hx-swap-oob="true">
  4 élément(s)
</span>
Démo live — regarde le compteur ET la liste se mettre à jour ensemble

Compteur (ailleurs dans le DOM) : 1 élément(s)

    9. Éviter les réponses dans le désordre hx-sync

    Le serveur simule ici un délai aléatoire (200 à 1800ms). Sans synchronisation, si tu tapes vite, une réponse lente à une frappe ancienne pourrait arriver après une réponse plus récente et écraser le bon résultat par un résultat obsolète. hx-sync="this:replace" annule automatiquement la requête en cours dès qu'une nouvelle est déclenchée sur le même élément : seule la dernière réponse compte.

    <input name="q"
           hx-get="index.php?action=sync-search"
           hx-trigger="keyup changed delay:100ms"
           hx-sync="this:replace"
           hx-target="#sync-result">
    Démo live — tape vite plusieurs lettres d'affilée
    En attente…

    10. N'extraire qu'un fragment de la réponse hx-select

    Le serveur renvoie volontairement 3 blocs, mais grâce à hx-select, HTMX n'insère dans la page que celui dont le sélecteur CSS correspond — pratique pour réutiliser un endpoint qui rend "une page complète" (avec header/footer) tout en n'injectant qu'un extrait précis côté client.

    <button hx-get="index.php?action=select-fragment"
            hx-select="#useful-part"
            hx-target="#select-result">
      Charger seulement le bloc utile
    </button>
    Démo live
    Rien chargé pour l'instant — clique pour voir que seul un fragment est inséré.

    11. Temps réel avec Server-Sent Events hx-ext="sse"

    Le serveur ouvre un flux text/event-stream et pousse des évènements au fil de l'eau ; HTMX les injecte dans le DOM au fur et à mesure, sans polling côté client. Utile pour des notifications, une progress bar, un flux de logs… La démo s'arrête après 6 évènements et envoie un évènement close explicite pour éviter que le navigateur ne reconnecte tout seul (comportement par défaut d'EventSource). Elle démarre au clic, pas au chargement de la page : le serveur PHP intégré est mono-thread, donc une connexion ouverte en arrière-plan bloquerait toutes les autres démos de la page tant qu'elle n'est pas terminée.

    <div hx-ext="sse" sse-connect="index.php?action=sse-stream">
      <div sse-swap="tick" hx-swap="beforeend"></div>
    </div>
    Démo live — clique pour ouvrir le flux (bloque le reste de la page ~6s, c'est normal avec le serveur de dev)

    12. Navigation façon SPA hx-push-url

    Chaque lien charge un fragment de contenu et met à jour l'URL du navigateur (hx-push-url="true") sans rechargement complet. Le bouton retour du navigateur fonctionne normalement. En production, le serveur doit aussi savoir répondre correctement si quelqu'un charge cette URL directement (rafraîchit la page) — typiquement en inspectant l'en-tête HX-Request pour renvoyer soit la page complète, soit juste le fragment.

    <a hx-get="index.php?action=page&name=about"
       hx-target="#spa-content"
       hx-push-url="true">À propos</a>
    Démo live — regarde l'URL changer, puis teste le bouton "retour"

    🏠 Accueil

    Contenu de la page d'accueil, chargé sans rechargement complet.

    📌 Ce qu'il faut retenir

    HTMX étend le HTML avec des attributs (hx-get, hx-post, hx-put, hx-delete, hx-trigger, hx-target, hx-swap, hx-confirm, hx-indicator…). Le serveur renvoie des fragments HTML (pas du JSON) qui remplacent ou complètent une partie du DOM. Résultat : des interfaces dynamiques avec très peu (voire zéro) JavaScript custom, et toute la logique métier reste côté serveur (ici, en PHP).