„Wir brauchen React für diese Webapp” – dieser Satz fällt in vielen Projekten reflexartig. Doch für einen Großteil der Business-Anwendungen ist das technischer Overkill. Die meisten internen Tools, Admin-Panels und CRUD-Anwendungen kämpfen nicht mit komplexer UI-State-Logik, sondern mit Formularen, Tabellen, Filtern und einfachen Workflows.
Genau hier kommt HTMX ins Spiel: Eine leichtgewichtige JavaScript-Bibliothek, die serverseitiges HTML mit modernen Interaktionsmustern verbindet – ohne dass man eine komplette SPA bauen muss.
Dieser Artikel zeigt, wann HTMX die richtige Wahl ist, wo es an Grenzen stößt und wie man damit in der Praxis arbeitet.
Was ist HTMX überhaupt?
HTMX ist eine JavaScript-Bibliothek (~14 KB gzipped), die es ermöglicht, moderne Web-Interaktionen direkt über HTML-Attribute zu steuern – ohne eigenes JavaScript schreiben zu müssen.
Das Grundprinzip:
Statt bei jedem Klick eine JSON-API aufzurufen, parst der Client die Response und rendert dann die UI neu (wie bei SPAs üblich), schickt der Server bei HTMX direkt fertig gerenderte HTML-Fragmente zurück. Diese werden dann an der richtigen Stelle im DOM eingefügt oder ersetzt.
Ein einfaches Beispiel:
<button hx-get="/load-more" hx-target="#results" hx-swap="beforeend">
Mehr laden
</button>
<div id="results">
<!-- Hier werden weitere Ergebnisse eingefügt -->
</div>
Was passiert:
- User klickt auf den Button
- HTMX schickt GET-Request an
/load-more - Server antwortet mit HTML-Fragment (z.B.
<div>Neuer Eintrag</div>) - HTMX fügt das Fragment ans Ende von
#resultsein
Kein JSON-Parsing, kein Client-Side-Rendering, kein React-State-Management.
Die wichtigsten HTMX-Konzepte
HTTP-Methoden über Attribute
HTMX unterstützt alle gängigen HTTP-Methoden direkt über HTML-Attribute:
<!-- GET Request -->
<div hx-get="/api/users" hx-trigger="load">
<!-- Wird beim Laden der Seite gefüllt -->
</div>
<!-- POST Request -->
<form hx-post="/api/users" hx-target="#message">
<input name="username" />
<button type="submit">Speichern</button>
</form>
<!-- DELETE Request -->
<button hx-delete="/api/users/123" hx-confirm="Wirklich löschen?">
Löschen
</button>
<!-- PUT/PATCH Request -->
<form hx-put="/api/users/123">
<input name="status" value="active" />
<button type="submit">Status ändern</button>
</form>
Trigger: Wann werden Requests ausgelöst?
Mit hx-trigger steuert man, bei welchem Event eine Anfrage gefeuert wird:
<!-- Standard: Click bei Buttons -->
<button hx-get="/data" hx-trigger="click">Laden</button>
<!-- Bei Formular-Änderung mit Verzögerung (Live-Suche) -->
<input hx-get="/search"
hx-trigger="keyup changed delay:300ms"
hx-target="#results" />
<!-- Nur einmalig -->
<div hx-get="/init" hx-trigger="load once"></div>
<!-- Polling alle 5 Sekunden -->
<div hx-get="/status" hx-trigger="every 5s"></div>
<!-- Bei Sichtbarkeit (Infinite Scroll) -->
<div hx-get="/load-more" hx-trigger="revealed"></div>
<!-- Event-Filter: Nur mit Shift-Taste -->
<button hx-get="/admin" hx-trigger="click[shiftKey]">Admin</button>
<!-- Mehrere Events -->
<div hx-get="/data" hx-trigger="click, mouseover"></div>
Swap-Strategien: Wie wird HTML eingefügt?
Mit hx-swap bestimmt man, wie die Server-Response ins DOM kommt:
<!-- Ersetzt innerHTML (default) -->
<div hx-get="/content" hx-swap="innerHTML"></div>
<!-- Ersetzt das gesamte Element -->
<div hx-get="/content" hx-swap="outerHTML"></div>
<!-- Fügt vor dem Element ein -->
<div hx-get="/content" hx-swap="beforebegin"></div>
<!-- Fügt am Anfang des Elements ein -->
<div hx-get="/content" hx-swap="afterbegin"></div>
<!-- Fügt am Ende des Elements ein (z.B. für Listen) -->
<div hx-get="/content" hx-swap="beforeend"></div>
<!-- Fügt nach dem Element ein -->
<div hx-get="/content" hx-swap="afterend"></div>
<!-- Löscht das Element -->
<div hx-delete="/item/123" hx-swap="delete"></div>
<!-- Tut nichts (nur für Side-Effects) -->
<div hx-post="/analytics" hx-swap="none"></div>
Weitere wichtige Attribute
<!-- Target: Wohin soll die Response? -->
<button hx-get="/data" hx-target="#output">Laden</button>
<div id="output"></div>
<!-- Include: Welche Formularfelder sollen mitgeschickt werden? -->
<button hx-post="/save" hx-include="#form-data">Speichern</button>
<!-- Push URL: Browser-History aktualisieren -->
<a hx-get="/page2" hx-push-url="true">Seite 2</a>
<!-- Confirm: Bestätigung vor Request -->
<button hx-delete="/user/123" hx-confirm="Wirklich löschen?">Löschen</button>
Wann ist HTMX die richtige Wahl?
1. Die meisten Business-Apps sind CRUD
Typische Anwendungsfälle:
- Admin-Panels
- Interne Tools
- Backoffice-Systeme
- Formulare und Datenerfassung
- Reporting-Dashboards
- Content-Management
Was diese gemeinsam haben:
- Formulare (Create/Update)
- Tabellen/Listen (Read)
- Filter und Sortierung
- Einfache Workflows
- Wenig komplexe UI-Logik
Für solche Apps ist eine vollwertige SPA oft technischer Overkill.
Beispiel: Typisches Admin-Panel
<!-- Benutzerliste mit Suche -->
<input type="text"
hx-get="/users/search"
hx-trigger="keyup changed delay:300ms"
hx-target="#user-list"
placeholder="Suche..." />
<table id="user-list">
<!-- Serverseitig gerenderte Tabelle -->
</table>
<!-- Inline-Edit -->
<tr hx-get="/users/123/edit" hx-target="this" hx-swap="outerHTML">
<td>Max Mustermann</td>
<td>[email protected]</td>
<td><button>Bearbeiten</button></td>
</tr>
<!-- Nach Klick wird die Zeile durch ein Formular ersetzt -->
Server-Response (bei /users/123/edit):
<tr>
<td><input name="name" value="Max Mustermann" /></td>
<td><input name="email" value="[email protected]" /></td>
<td>
<button hx-put="/users/123" hx-target="closest tr">Speichern</button>
<button hx-get="/users/123" hx-target="closest tr">Abbrechen</button>
</td>
</tr>
Kein Client-Side-State, keine komplexen Validierungslogiken im Frontend – alles serverseitig gehandhabt.
2. Weniger JavaScript = bessere Performance
Typische React-App (mittlere Komplexität):
- React + ReactDOM: ~140 KB (gzipped)
- Router: ~10 KB
- State Management (Redux/Zustand): ~5-15 KB
- UI-Library (Material-UI/Ant Design): ~100-300 KB
- Eigener Code: ~50-200 KB
Gesamt: 300-700 KB JavaScript – und das vor dem ersten interaktiven Element.
HTMX-Alternative:
- HTMX: ~14 KB (gzipped)
- Optional: Alpine.js für lokale UI-Interaktionen: ~15 KB
- Eigener Code: minimal
Gesamt: ~30-50 KB JavaScript.
Was das bedeutet:
| Metrik | React SPA | HTMX |
|---|---|---|
| Bundle Size | 300-700 KB | 30-50 KB |
| Time to Interactive | 2-5 Sekunden | 0.5-1 Sekunde |
| Mobile Performance | Oft träge | Deutlich schneller |
| Build-Zeit | 30-120 Sekunden | Keine (serverseitig) |
Besonders auf mobilen Geräten oder bei schlechter Netzverbindung ist der Unterschied enorm.
3. Framework-Overhead ist real
React-Apps bringen nicht nur Bundle-Size, sondern auch Komplexität im Projekt:
Typische React-Setup-Entscheidungen:
- Welches State-Management? (Redux, Zustand, Jotai, Recoil?)
- Welcher Router? (React Router, TanStack Router, Wouter?)
- Server-State-Management? (React Query, SWR, Apollo?)
- Form-Handling? (React Hook Form, Formik?)
- Styling? (CSS Modules, Styled Components, Tailwind, Emotion?)
- Build-Tool? (Vite, Webpack, Turbopack?)
- Testing? (Jest, Vitest, Testing Library?)
Bei HTMX:
- Serverseitige Templates (Jinja, ERB, Blade, Razor, etc.)
- HTML zurückgeben
- Fertig.
Kein Build-Prozess, keine Abhängigkeiten-Hölle, kein “JS-Fatigue”.
4. SSR-first ist zurück
Moderne Backend-Frameworks setzen wieder auf Server-Side Rendering:
Rails: Hotwire (Turbo + Stimulus) Phoenix (Elixir): LiveView Laravel: Livewire Django: HTMX + django-template-partials ASP.NET: Razor Pages + HTMX Astro: SSR Mode + HTMX (perfekt für Content-Sites)
Warum?
- SEO by default: Alles ist HTML, Crawler lieben es
- Schnellere erste Anzeige: Kein Warten auf JavaScript-Parsing
- Einfacheres Deployment: Kein separates Frontend-Backend-Setup
- Weniger Komplexität: Ein Codebase, ein Deployment
Warum Astro + HTMX besonders gut passt:
- Astro ist bereits auf SSR ausgelegt
- Islands Architecture: Nur interaktive Teile sind JavaScript
- HTMX ergänzt perfekt: Dynamik ohne Client-Bundle
- Ideal für Content-heavy Sites (Blogs, Docs, Marketing)
Beispiel: Astro SSR + HTMX
---
// pages/products/[id].astro
const { id } = Astro.params;
const product = await db.getProduct(id);
---
<div>
<h1>{product.name}</h1>
<p>{product.price}</p>
<!-- HTMX für "In den Warenkorb" -->
<button
hx-post={`/cart/add/${id}`}
hx-target="#cart-count"
hx-swap="innerHTML">
In den Warenkorb
</button>
</div>
<div id="cart-count">{cartCount}</div>
Vorteil:
- Initiales HTML mit echten Daten (SEO!)
- HTMX für Interaktionen
- Kein React-Bundle nötig
Praxisbeispiele: Wo HTMX glänzt
Beispiel 1: Live-Suche
Problem: Nutzer tippt in ein Suchfeld, Ergebnisse sollen live aktualisiert werden.
HTMX-Lösung:
<input type="text"
name="q"
hx-get="/search"
hx-trigger="keyup changed delay:300ms"
hx-target="#results"
placeholder="Suche..." />
<div id="results">
<!-- Ergebnisse werden hier eingefügt -->
</div>
Server (z.B. Flask/Python):
@app.route('/search')
def search():
query = request.args.get('q', '')
results = db.search(query)
return render_template('search_results.html', results=results)
search_results.html:
{% for result in results %}
<div class="result">
<h3>{{ result.title }}</h3>
<p>{{ result.description }}</p>
</div>
{% endfor %}
Ein konkretes Beispiel: Die Suche auf Insights
Genau diese Suche hier im Blog ist aktuell mit ~200 Zeilen TypeScript umgesetzt: Der komplette Suchindex (alle Artikel-Metadaten) wird als JSON geladen, Client-seitig gefiltert, Ergebnisse werden im Browser gerendert. Das funktioniert, bedeutet aber erheblichen JavaScript-Code, der erst geladen, geparst und ausgeführt werden muss – besonders auf mobilen Geräten spürbar.
Mit HTMX würde dieselbe Suche so aussehen: Nutzer tippt, nach 300ms geht die Anfrage an den Server (z.B. /api/search), dieser durchsucht die Artikel und schickt fertig formatiertes HTML zurück. Die 200 Zeilen TypeScript werden zu 5 Zeilen HTMX-Markup. Die Suche wäre schneller, würde weniger Akku verbrauchen und funktioniert auch bei deaktiviertem JavaScript in einer Basisversion.
Da Insights bereits auf Astro läuft und auf Cloudflare Pages deployt ist, wäre das technisch problemlos umsetzbar: Astro SSR aktivieren, einen Search-Endpoint schreiben, fertig.
Warum nicht der ganze Blog mit HTMX?
Der Rest von Insights ist bewusst als statische Website gebaut – und das ist genau richtig so. Alle Artikel-Seiten werden beim Build als fertiges HTML generiert und können direkt vom CDN ausgeliefert werden. Das ist extrem schnell, günstig im Hosting und perfekt für SEO.
HTMX würde hier keinen Mehrwert bringen: Die Inhalte sind statisch, brauchen keine Interaktivität und sollen vom Crawler vollständig indexiert werden. Eine statische Seite ist hier die beste Lösung – kein Server-Roundtrip, keine zusätzliche Latenz, maximale Cache-Effizienz.
Die Suche ist der einzige dynamische Teil, der wirklich von HTMX profitieren würde. Das ist ein perfektes Beispiel für einen hybriden Ansatz: Statisches HTML für Content, HTMX nur dort, wo echte Interaktivität gefragt ist.
Vorteile:
- Kein JSON-API-Design nötig
- Keine Client-Side-Template-Engine
- Serverseitige Validierung und Filterung
- SEO-freundlich (erste Ergebnisse sind im initialen HTML)
- Deutlich weniger Client-Code (von ~200 Zeilen auf ~5 Zeilen)
Beispiel 2: Infinite Scroll
Problem: Beim Scrollen sollen weitere Einträge nachgeladen werden.
HTMX-Lösung:
<div id="items">
<!-- Erste 20 Einträge serverseitig gerendert -->
{% for item in items %}
<div class="item">{{ item.name }}</div>
{% endfor %}
</div>
<div hx-get="/items?page=2"
hx-trigger="revealed"
hx-swap="afterend">
<span class="loading">Lädt mehr...</span>
</div>
Server:
@app.route('/items')
def items():
page = int(request.args.get('page', 1))
items = db.get_items(page=page, per_page=20)
next_page = page + 1 if len(items) == 20 else None
return render_template('items_partial.html',
items=items,
next_page=next_page)
items_partial.html:
{% for item in items %}
<div class="item">{{ item.name }}</div>
{% endfor %}
{% if next_page %}
<div hx-get="/items?page={{ next_page }}"
hx-trigger="revealed"
hx-swap="afterend">
<span class="loading">Lädt mehr...</span>
</div>
{% endif %}
Magisch: Der Trigger revealed feuert automatisch, wenn das Element sichtbar wird. Perfekt für Infinite Scroll.
Beispiel 3: Inline-Editing in Tabellen
Problem: User soll direkt in einer Tabelle Werte bearbeiten können.
HTMX-Lösung:
<table>
<tr hx-target="this" hx-swap="outerHTML">
<td>Product A</td>
<td>49,99 €</td>
<td>
<button hx-get="/products/1/edit">Bearbeiten</button>
</td>
</tr>
</table>
Server-Response bei GET /products/1/edit:
<tr hx-target="this" hx-swap="outerHTML">
<td><input name="name" value="Product A" /></td>
<td><input name="price" value="49.99" type="number" /></td>
<td>
<button hx-put="/products/1" hx-include="closest tr">Speichern</button>
<button hx-get="/products/1">Abbrechen</button>
</td>
</tr>
Nach PUT /products/1 (Speichern):
<tr hx-target="this" hx-swap="outerHTML">
<td>Product A (Updated)</td>
<td>39,99 €</td>
<td>
<button hx-get="/products/1/edit">Bearbeiten</button>
</td>
</tr>
Resultat: Inline-Editing ohne eine Zeile eigenes JavaScript.
Beispiel 4: Modal-Dialog laden
Problem: Klick auf Button soll Modal mit Server-Content öffnen.
HTMX-Lösung:
<button hx-get="/user/123/details"
hx-target="#modal-content"
hx-trigger="click"
onclick="document.getElementById('modal').style.display='block'">
Details anzeigen
</button>
<div id="modal" style="display:none;">
<div id="modal-content">
<!-- Server-Content wird hier eingefügt -->
</div>
<button onclick="document.getElementById('modal').style.display='none'">
Schließen
</button>
</div>
Server-Response:
<div>
<h2>Max Mustermann</h2>
<p>Email: [email protected]</p>
<p>Registriert: 01.01.2024</p>
</div>
Noch besser mit Alpine.js:
<div x-data="{ open: false }">
<button hx-get="/user/123/details"
hx-target="#modal-content"
@click="open = true">
Details anzeigen
</button>
<div x-show="open" @click.away="open = false" class="modal">
<div id="modal-content"></div>
</div>
</div>
Wo HTMX an Grenzen stößt
HTMX ist nicht für alles die beste Lösung. Hier sind typische Limitierungen:
1. Hochkomplexe UI-State-Logik
Problematisch:
- Echtzeit-Kollaboration (Google Docs, Figma)
- Komplexe Formulare mit verschachtelter Validierung und bedingten Feldern
- Drag-and-Drop mit komplexen Regeln
- Offline-First-Apps
Warum: HTMX hat keinen eigenen State-Manager. Jede Interaktion ist ein Server-Roundtrip. Bei Apps, wo viel lokale UI-Logik nötig ist, wird das schnell unhandlich.
Lösung: Für solche Fälle sind React, Vue, Svelte die bessere Wahl – oder ein hybrider Ansatz (HTMX für CRUD, React für komplexe UI-Teile).
2. Sehr hohe Interaktivität
Problematisch:
- Spiele
- Karteninteraktionen (Google Maps)
- Rich-Text-Editoren
- Echtzeit-Charts mit vielen Updates
Warum: Jeder HTMX-Request bedeutet Server-Roundtrip. Bei sehr vielen Interaktionen pro Sekunde wird das ineffizient.
Lösung: Hybride Ansätze: HTMX für das Grundgerüst, JavaScript-Komponenten (z.B. Chart.js, Leaflet) für hochinteraktive Teile.
3. Offline-Funktionalität
Problematisch:
- Progressive Web Apps (PWAs) mit Offline-Sync
- Apps für Regionen mit schlechtem Netz
Warum: HTMX ist Server-abhängig. Ohne Verbindung funktioniert nichts.
Lösung: Service Worker + lokale Caches können helfen, aber dann verliert man HTMXs Einfachheit. Für echte Offline-Apps sind SPAs besser geeignet.
4. SEO für hochdynamische Inhalte
Problematisch:
- Content, der erst nach User-Interaktion geladen wird
- Deep-Linking in dynamisch geladene Bereiche
Warum: Crawler sehen nur das initiale HTML. Dynamisch nachgeladene Inhalte (z.B. Tabs, Accordions) werden oft nicht indexiert.
Lösung:
- Initiales HTML sollte alle wichtigen Inhalte enthalten
hx-push-urlnutzen für Navigation- Server-Side-Rendering für alle indexierbaren Inhalte
5. Team-Skill-Set
Problematisch:
- Team hat bereits viel React-Expertise
- Projekt nutzt bereits React-Ökosystem (React Native App, Shared Components)
Warum: HTMX erfordert Umdenken: Weniger Client-Side-Logik, mehr serverseitiges Rendering. Wenn das Team bereits React kann und das Projekt davon profitiert (z.B. Code-Sharing mit Mobile), ist der Wechsel zu HTMX kontraproduktiv.
Hybride Ansätze: Das Beste aus beiden Welten
Oft ist die Lösung nicht entweder/oder, sondern sowohl als auch.
Ansatz 1: HTMX + Alpine.js
Idee: HTMX für Server-Kommunikation, Alpine.js für lokale UI-Interaktionen.
Beispiel: Dropdown mit Filter
<div x-data="{ open: false }">
<button @click="open = !open">Filter öffnen</button>
<div x-show="open" @click.away="open = false">
<input hx-get="/filter"
hx-trigger="keyup changed delay:300ms"
hx-target="#results"
x-ref="search" />
</div>
</div>
<div id="results"></div>
Vorteil:
- Alpine für lokale State (Dropdown-Toggle)
- HTMX für Server-Requests
- Nur ~30 KB JavaScript insgesamt
Ansatz 2: HTMX für CRUD, React für komplexe Komponenten
Idee: Standard-CRUD mit HTMX, einzelne komplexe Komponenten mit React.
Beispiel:
- Admin-Panel: HTMX (Formulare, Tabellen, Navigation)
- Reporting-Dashboard: React (interaktive Charts, Echtzeit-Updates)
- Kalender-Widget: React (Drag-and-Drop)
Umsetzung:
<!-- HTMX-basierte Seite -->
<div hx-get="/dashboard" hx-target="#content">
<div id="content">
<!-- Serverseitiges HTML -->
<!-- React-Komponente einbetten -->
<div id="chart-root"></div>
<script>
ReactDOM.render(<ChartComponent />, document.getElementById('chart-root'));
</script>
</div>
</div>
Vorteil:
- Das Beste aus beiden Welten
- Nur da Komplexität, wo nötig
- Schrittweise Migration möglich
Performance-Vergleich: Real-World-Zahlen
Szenario: Admin-Panel mit 50 Seiten, 200 Formularen, 30 Tabellen.
| Metrik | React SPA | HTMX |
|---|---|---|
| Initial Bundle | 450 KB | 14 KB |
| Time to Interactive | 3,2 Sekunden | 0,7 Sekunden |
| Lighthouse Score | 72 | 94 |
| Build Time | 45 Sekunden | 0 Sekunden |
| Server Load | Niedrig (nur API) | Mittel (HTML-Rendering) |
| Cache-Strategie | Komplex (SWR, React Query) | Simpel (HTTP-Cache) |
Fazit: HTMX ist deutlich schneller beim initialen Load, React ist besser bei wiederholten Interaktionen (da weniger Server-Roundtrips).
Wann sollte man HTMX wählen?
HTMX ist ideal für:
✅ CRUD-lastige Anwendungen (Admin-Panels, Backoffice-Tools) ✅ Formulare und Datenerfassung ✅ Tabellen, Filter, Sortierung ✅ Prototypen und MVPs (schnelle Umsetzung) ✅ Teams mit Backend-Fokus (weniger JS-Expertise nötig) ✅ Performance-kritische Apps (Mobile, langsames Netz) ✅ SEO-relevante Inhalte (Server-Side-Rendering by default) ✅ Content + Interaktivität (Astro SSR + HTMX für Blogs/Docs mit dynamischen Features)
Besonders stark mit SSR-Frameworks:
- Astro: Content-Sites mit partieller Interaktivität
- Rails/Laravel: Full-Stack Web-Apps
- Phoenix LiveView: Echtzeit ohne WebSocket-Komplexität
React/SPA ist besser für:
❌ Hochinteraktive UIs (Echtzeit-Kollaboration, Spiele) ❌ Offline-First-Apps (PWAs mit Sync) ❌ Komplexe UI-State-Logik (viele verschachtelte Abhängigkeiten) ❌ Apps mit Mobile-Pendant (Code-Sharing mit React Native) ❌ Teams mit starkem Frontend-Fokus (bereits viel React-Expertise)
Tools und Erweiterungen
HTMX Extensions
HTMX bietet offizielle Extensions für spezielle Use Cases:
- SSE (Server-Sent Events): Echtzeit-Updates vom Server
- WebSocket: Bidirektionale Echtzeit-Kommunikation
- Preload: Prefetching für schnellere Navigation
- Morphdom: Intelligentes DOM-Patching (ähnlich React)
- Class Tools: CSS-Klassen bei Events hinzufügen/entfernen
Kompatible Frontend-Tools
- Alpine.js: Perfekte Ergänzung für lokale UI-State
- Hyperscript: Deklarative Event-Handler (Alternative zu JavaScript)
- _hyperscript: Noch expressivere Syntax
Backend-Integrationen
Astro (SSR Mode)
- Native HTMX-Integration
- Islands Architecture + HTMX = minimales JavaScript
- Perfekt für Content + E-Commerce Hybrid
- Deployment: Cloudflare, Vercel, Netlify
---
// Astro Endpoint für HTMX
export async function POST({ request }) {
const formData = await request.formData();
const email = formData.get('email');
// Server-Side Validierung
if (!isValidEmail(email)) {
return new Response(
'<p class="error">Ungültige E-Mail</p>',
{ headers: { 'Content-Type': 'text/html' } }
);
}
await subscribe(email);
return new Response(
'<p class="success">✓ Erfolgreich angemeldet!</p>',
{ headers: { 'Content-Type': 'text/html' } }
);
}
---
Django: django-htmx (Middleware + Template-Tags) Rails: Turbo + Stimulus (ähnliches Konzept) Laravel: Livewire (Wire-Protokoll statt HTMX) ASP.NET: Htmx.Net (Tag Helpers)
Best Practices
1. Klare URL-Struktur
Gut:
GET /users → Liste
GET /users/123 → Detail
GET /users/123/edit → Edit-Formular
POST /users → Create
PUT /users/123 → Update
DELETE /users/123 → Delete
Wichtig: REST-Prinzipien helfen, Code sauber zu halten.
2. Konsistente Response-Struktur
Immer gleiche HTML-Struktur für partials:
<!-- Alle Listen-Items haben gleiche Struktur -->
<div class="list-item" id="item-{{ item.id }}">
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
<button hx-delete="/items/{{ item.id }}" hx-target="closest .list-item">
Löschen
</button>
</div>
3. Loading-States zeigen
HTMX hat eingebaute CSS-Klassen:
/* Während Request läuft */
.htmx-request {
opacity: 0.5;
cursor: wait;
}
/* Beim Swapping */
.htmx-swapping {
opacity: 0;
transition: opacity 200ms;
}
Custom Indicator:
<button hx-get="/slow-endpoint">
<span class="htmx-indicator">
<span class="spinner"></span>
</span>
Laden
</button>
4. Fehlerbehandlung
Server sollte im Fehlerfall auch HTML zurückgeben:
@app.route('/users', methods=['POST'])
def create_user():
try:
user = create_user_from_form()
return render_template('user_row.html', user=user)
except ValidationError as e:
response = make_response(
render_template('error_message.html', error=e.message)
)
response.status_code = 422 # Unprocessable Entity
return response
HTMX zeigt Fehler automatisch im Target:
<form hx-post="/users" hx-target="#message">
<input name="email" />
<button>Speichern</button>
</form>
<div id="message">
<!-- Erfolg oder Fehler wird hier angezeigt -->
</div>
Nicht länger warten auf das perfekte Framework
Die Webentwicklung hat sich in den letzten Jahren stark in Richtung Client-Side-Heavy-SPAs entwickelt. Das war für viele Use Cases übertrieben.
HTMX bringt zurück, was funktioniert:
- Server rendert HTML
- Browser zeigt HTML an
- Bei Interaktion: Server schickt neues HTML
Simpel, schnell, wartbar.
Die wichtigste Erkenntnis: Nicht jede Webapp braucht React. Für die meisten Business-Anwendungen ist HTMX die pragmatischere, schnellere und einfachere Lösung.
Der beste Startpunkt: Nächstes internes Tool, nächstes Admin-Panel, nächster Prototyp – probier HTMX aus. Du wirst überrascht sein, wie wenig JavaScript du wirklich brauchst.