HTMX Troubleshooting

Erstellt: 2025-12-27 | Aktualisiert: 2025-12-31

Häufige Probleme

1. CSRF-Token Fehler (403)

SymptomRequest schlägt mit 403 fehl
Ursachehx-headers fehlt oder Token ist ungültig
Lösunghx-headers='{"X-CSRF-TOKEN": "<?= $csrfToken ?>"}'

Prüfen:

2. hx-indicator zeigt nichts an

SymptomLoading-Spinner erscheint nicht
Ursachehx-indicator zeigt auf Element, CSS fehlt

Lösung:

<!-- Indicator braucht ID oder Selektor -->
<button hx-indicator="#my-spinner">...</button>
<span id="my-spinner" class="htmx-indicator">Loading...</span>
/* CSS muss vorhanden sein */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline; }

3. Response wird nicht angezeigt

SymptomRequest erfolgreich, aber nichts passiert
Ursachen
  1. hx-target zeigt auf nicht existierendes Element
  2. hx-swap="none" gesetzt
  3. Response ist leer

Debugging:

<!-- Temporär hinzufügen -->
hx-on::after-request="console.log('Response:', event.detail.xhr.responseText)"

4. Button bleibt disabled

SymptomNach Request bleibt Button deaktiviert
Ursachehx-disabled-elt wird nicht zurückgesetzt bei Fehler
LösungHTMX macht das automatisch. Prüfen ob JS den Button manuell disabled.

5. Doppelte Requests

SymptomEin Klick löst mehrere Requests aus
Ursachen
  1. Event-Bubbling (Button in Form)
  2. Mehrere HTMX-Attribute matchen

Lösung:

<!-- Bei Button in Form -->
<button type="button" hx-post="...">  <!-- type="button" statt submit -->

<!-- Oder -->
<form hx-post="..." hx-trigger="submit">
    <button type="submit">Submit</button>  <!-- Nur form hat hx-post -->
</form>

6. Form-Daten werden nicht gesendet

SymptomPOST-Request hat leeren Body
Ursachehx-* auf Button statt Form

Falsch:

<form>
    <input name="title">
    <button hx-post="/api">Send</button>
</form>

Richtig:

<form hx-post="/api">
    <input name="title">
    <button type="submit">Send</button>
</form>

7. History/Back-Button Probleme

SymptomBrowser-Back funktioniert nicht wie erwartet
UrsacheHTMX ersetzt Inhalte ohne History-Push

Lösung:

<!-- Für Navigation, die History braucht -->
<a href="/page" hx-push-url="true" hx-get="/page" hx-target="#main">Link</a>

<!-- Oder: Normaler Link ohne HTMX -->
<a href="/page">Link</a>

Debugging-Tools

1. HTMX Debug-Extension

<script src="https://unpkg.com/htmx.org/dist/ext/debug.js"></script>
<body hx-ext="debug">

Loggt alle HTMX-Events in die Console.

2. Request/Response in DevTools

  1. DevTools → Network
  2. XHR/Fetch filtern
  3. Request Headers prüfen (X-CSRF-TOKEN, HX-Request)
  4. Response Body prüfen

3. HTMX Events manuell loggen

document.body.addEventListener('htmx:beforeRequest', (e) => {
    console.log('Before:', e.detail);
});

document.body.addEventListener('htmx:afterRequest', (e) => {
    console.log('After:', e.detail);
    console.log('Response:', e.detail.xhr.responseText);
});

document.body.addEventListener('htmx:responseError', (e) => {
    console.error('Error:', e.detail);
});

Contract-Validierung Fehlermeldungen

Bei Contract-Validierung (contracts_validate(name="htmx-patterns")) können folgende Fehler auftreten:

"hx-post missing CSRF token"

BedeutungHTMX-C1 verletzt
Fixhx-headers='{"X-CSRF-TOKEN": "<?= $csrfToken ?>"}'

"hx-delete missing confirmation"

BedeutungHTMX-C4 verletzt
Fixhx-confirm="Wirklich löschen?"

Best Practices

1. Immer explizites Target

<!-- Gut -->
hx-target="#result-container"

<!-- Vermeiden (ersetzt Element selbst) -->
hx-target="this"

2. Expliziter Swap-Modus

<!-- Gut -->
hx-swap="innerHTML"

<!-- Vermeiden (Default ist innerHTML, aber explizit ist klarer) -->
(kein hx-swap)

3. Feedback bei hx-swap="none"

<!-- Bei hx-swap="none" braucht User visuelles Feedback -->
<input hx-swap="none"
       hx-on::after-request="this.classList.add('is-saved'); setTimeout(() => this.classList.remove('is-saved'), 1000)">

Verwandte Themen