---
title: 'HTMX : charger un fragment HTML avec hx-get'
description: 'Crée un exemple local avec hx-get, hx-target et hx-swap : un bouton appelle un serveur Python et remplace une zone de la page par du HTML.'
pubDate: 2025-08-22
updatedDate: 2026-10-18
family: 'javascript-frontend'
tags: []
heroImage: '/images/apprendre-htmx-1-introduction-a-htmx-hx-get.webp'
videoUrl: 'https://youtu.be/u8M8y8GRUZA'
---

Tu veux charger un message sans recharger toute la page ? Avec HTMX, tu peux déclarer la requête
sur un bouton et laisser le serveur produire le HTML à afficher. Ce guide construit une démonstration
locale complète : une page, un endpoint et une zone mise à jour.

Cette mise à jour ajoute un exemple autonome et ses contrôles à l'article initial. Elle s'appuie sur
la documentation officielle, sans prétendre retranscrire la vidéo associée.

## Comprendre les trois attributs

- [`hx-get`](https://htmx.org/attributes/hx-get/) indique l'URL appelée en GET. Sur le bouton de cet
  exemple, un clic déclenche la requête.
- [`hx-target`](https://htmx.org/attributes/hx-target/) désigne l'élément à mettre à jour, ici
  le sélecteur CSS `#resultat`.
- [`hx-swap`](https://htmx.org/attributes/hx-swap/) choisit comment insérer la réponse. Avec
  `innerHTML`, tu remplaces le contenu de la cible, pas son conteneur.

Le serveur renvoie donc un **fragment HTML**, pas un objet JSON que HTMX transformerait automatiquement
en interface. Tu conserves la responsabilité du rendu côté serveur.

## Préparer un dossier de démonstration

Il te faut Python 3, Node.js avec npm et curl pour les contrôles HTTP. Dans un dossier vide :

```bash
mkdir demo-htmx
cd demo-htmx
npm init -y
npm install htmx.org@2.0.11
cp node_modules/htmx.org/dist/htmx.min.js htmx.min.js
```

La version est volontairement figée pour reproduire l'exemple ; ce n'est pas une promesse de « dernière
version ». La [documentation d'installation HTMX](https://htmx.org/docs/#installing) référence ce paquet
et ses fichiers de distribution. Le script sera servi localement : aucun CDN n'est nécessaire.

## Créer le serveur et la page

Enregistre ce code dans `app.py`, à côté de `htmx.min.js` :

```python
from html import escape
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from urllib.parse import parse_qs, urlsplit

PAGE = """<!doctype html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <title>Premier fragment HTMX</title>
  <script src="/htmx.min.js" defer></script>
</head>
<body>
  <h1>Charger un message</h1>
  <button type="button"
          hx-get="/bonjour?nom=Camille"
          hx-target="#resultat"
          hx-swap="innerHTML">
    Charger le message
  </button>
  <div id="resultat" aria-live="polite">Aucun message chargé.</div>
</body>
</html>"""


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        url = urlsplit(self.path)
        content_type = "text/html; charset=utf-8"
        if url.path == "/":
            body = PAGE.encode("utf-8")
        elif url.path == "/htmx.min.js":
            body = Path(__file__).with_name("htmx.min.js").read_bytes()
            content_type = "text/javascript; charset=utf-8"
        elif url.path == "/bonjour":
            name = parse_qs(url.query).get("nom", ["toi"])[0]
            body = f"<p>Bonjour {escape(name)} !</p>".encode("utf-8")
        else:
            self.send_error(404)
            return
        self.send_response(200)
        self.send_header("Content-Type", content_type)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)


HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()
```

Lance-le depuis ce dossier :

```bash
python3 app.py
```

Ouvre `http://127.0.0.1:8000/` dans ton navigateur. Au clic, le résultat attendu est
« Bonjour Camille ! » à la place de « Aucun message chargé. ». Le bouton reste présent puisque
la cible est le bloc `#resultat`.

## Vérifier le fragment indépendamment du navigateur

Dans un autre terminal :

```bash
curl -i -H 'HX-Request: true' 'http://127.0.0.1:8000/bonjour?nom=Camille'
curl -s 'http://127.0.0.1:8000/bonjour?nom=%3Cscript%3E'
```

Le premier appel doit renvoyer HTTP 200, le type `text/html; charset=utf-8` et ce corps :

```html
<p>Bonjour Camille !</p>
```

Le second doit produire ce fragment échappé :

```html
<p>Bonjour &lt;script&gt; !</p>
```

La valeur reçue est traitée comme du texte, pas comme une balise.

**Vérification effectuée pour ce guide :** installation de HTMX 2.0.11 et exécution du serveur avec
Python 3.14.7 sous Linux ; ces deux requêtes curl ont renvoyé les corps indiqués. La page, le script
local et une route inconnue ont aussi été contrôlés (200, 200 et 404). Ces tests valident les réponses
HTTP et l'échappement de cet exemple, **pas le clic ni le remplacement du DOM dans un navigateur**.
L'en-tête `HX-Request` n'est pas utilisé par ce serveur : `/bonjour` renvoie toujours un fragment.

## Choisir le bon remplacement et diagnostiquer les erreurs

Avec [`hx-swap`](https://htmx.org/attributes/hx-swap/), `outerHTML` remplace aussi l'élément cible :
si ton fragment ne recrée pas son identifiant, le prochain clic ne retrouvera plus `#resultat`.
`beforeend` ajoute au contraire le fragment à la fin de la cible ; plusieurs clics accumulent les
messages. Garde `innerHTML` pour ce premier exemple.

Si rien ne change, vérifie dans les outils de développement que `/htmx.min.js` est chargé, que le
clic lance bien `/bonjour` et que la réponse contient du HTML. Un 404 indique une route manquante ;
un fragment reçu mais non affiché invite à vérifier le sélecteur et l'existence de la cible.
N'ouvre pas la page en `file://` : utilise l'adresse du serveur.

## Sécurité et limites de cette démonstration

Le [guide de sécurité HTMX](https://htmx.org/docs/#security) insiste sur le risque d'injecter du HTML
non fiable. Ici, `escape(name)` protège la valeur insérée dans le texte du paragraphe ; ce n'est pas
un assainisseur universel pour du HTML riche, du JavaScript ou des URL. En production, utilise le
mécanisme d'échappement contextuel de ton moteur de templates et ne renvoie pas directement du HTML
fourni par un utilisateur.

Cet exemple n'a ni compte utilisateur ni écriture en base. Il ne démontre donc pas l'authentification,
les autorisations ou la protection CSRF. L'en-tête `HX-Request` n'est pas une preuve d'identité.
Garde les opérations qui modifient des données hors de ce GET de lecture et applique les contrôles
côté serveur. Le serveur Python lié à l'interface locale sert uniquement à apprendre, pas à déployer
une application publique.

Tu peux aussi regarder la [vidéo associée](https://youtu.be/u8M8y8GRUZA) pour prolonger cette
introduction à HTMX.
