Zum Hauptinhalt springen
HTMX: Wann der Verzicht auf React & Co. die bessere Wahl ist
#HTMX #Webentwicklung #Performance #SSR #JavaScript

HTMX: Wann der Verzicht auf React & Co. die bessere Wahl ist


Warum serverseitiges Rendering mit HTML-Fragmenten für viele Business-Apps die pragmatischere Lösung ist – und wo die Grenzen liegen

13 Minuten Lesezeit

„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:

  1. User klickt auf den Button
  2. HTMX schickt GET-Request an /load-more
  3. Server antwortet mit HTML-Fragment (z.B. <div>Neuer Eintrag</div>)
  4. HTMX fügt das Fragment ans Ende von #results ein

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:

MetrikReact SPAHTMX
Bundle Size300-700 KB30-50 KB
Time to Interactive2-5 Sekunden0.5-1 Sekunde
Mobile PerformanceOft trägeDeutlich schneller
Build-Zeit30-120 SekundenKeine (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-url nutzen 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.

MetrikReact SPAHTMX
Initial Bundle450 KB14 KB
Time to Interactive3,2 Sekunden0,7 Sekunden
Lighthouse Score7294
Build Time45 Sekunden0 Sekunden
Server LoadNiedrig (nur API)Mittel (HTML-Rendering)
Cache-StrategieKomplex (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 DatenerfassungTabellen, Filter, SortierungPrototypen 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.