HTMX für React-Entwickler: Vom State-Management zurück zu Hypermedia

1. Einleitung: Die SPA-Müdigkeit und das Pendel der Webentwicklung

Wenn du in den letzten Jahren Web-Applikationen gebaut hast, kennst du den Drill: Du startest mit create-react-app, Vite oder Next.js. Du schreibst Komponenten, verwaltest Zustand mit useState oder Redux, synchronisierst den Server mit useEffect oder React Query und am Ende schickst du ein riesiges JavaScript-Bundle an den Client. Der Browser muss dieses Bundle herunterladen, parsen und ausführen, um ein nacktes <div id="root"></div> in eine funktionierende UI zu verwandeln (oder bei SSR: das statische HTML zu "hydrieren").

Sowohl Junior- als auch Senior-React-Entwickler spüren zunehmend eine gewisse "Fatigue" (Ermüdung). Die Komplexität des Frontend-Ökosystems ist explodiert. Wir haben das Rad neu erfunden: Routing, State Management, Data Fetching, Formular-Validierung – alles wurde vom Server in den Browser verschoben.

HTMX ist nicht einfach nur eine weitere Bibliothek. Es ist eine radikale Rückkehr zu den Wurzeln des Webs, gepaart mit den interaktiven Erwartungen von heute. HTMX macht HTML wieder zu einer vollwertigen Programmiersprache (Hypermedia) und eliminiert in vielen Anwendungsfällen die Notwendigkeit für JavaScript-fokussierte Single Page Applications (SPAs).

In diesem Artikel zerlegen wir HTMX aus der Perspektive eines React-Entwicklers. Wir betrachten das Mental Model, die Syntax, reale Use Cases, die Developer Experience (DX) und vor allem: Wo HTMX glänzt und wo es grandios scheitert.


2. Das Mental Model: React vs. HTMX

Um HTMX zu verstehen, musst du verlernen, wie SPAs funktionieren.

Der React-Weg: UI als Funktion des Zustands

In React ist die UI eine Projektion des lokalen Zustands (UI = f(state)).

  1. Der Client fordert JSON von einer REST- oder GraphQL-API an.
  2. Der Client speichert diese Daten im Speicher (Memory/Store).
  3. React berechnet den Virtual DOM.
  4. React mutiert den realen DOM.

Das Problem: Du hast jetzt zwei Zustände. Den echten Zustand (Source of Truth) auf der Datenbank/dem Server und eine temporäre Kopie davon in deinem Browser. Du verbringst 80 % deiner Zeit damit, diese beiden Zustände zu synchronisieren (Loading Spinners, Optimistic Updates, Cache Invalidation, Error Handling).

Der HTMX-Weg: HATEOAS und Hypermedia

HTMX basiert auf dem Prinzip HATEOAS (Hypermedia As The Engine Of Application State). In HTMX gibt es keinen Client-Side State im herkömmlichen Sinne. Es gibt keine JSON-APIs.

  1. Der Client feuert eine Interaktion (Klick, Hover, Input).
  2. Der Server berechnet den neuen Zustand und rendert exakt das Stückchen HTML, das sich ändert.
  3. Der Server sendet dieses HTML an den Client.
  4. HTMX nimmt das HTML und tauscht es an der richtigen Stelle im DOM aus.

Die Offenbarung: Der Zustand ist der DOM. Der Server ist die einzige Source of Truth. Wenn du ein To-Do abhakt, fragst du nicht nach einem JSON-Update, um dein lokales Array zu filtern. Du schickst einen POST-Request und der Server antwortet direkt mit der aktualisierten To-Do-Liste als HTML.


3. Kern-Syntax und "Locality of Behavior"

React hat uns gelehrt, dass die Trennung von HTML, CSS und JS oft künstlich ist (Separation of Technologies). React brachte uns "Locality of Behavior" (LoB) – alles, was eine Komponente ausmacht, lebt in einer Datei.

HTMX treibt LoB auf die Spitze. Du musst nicht mehr in eine useEffect-Hook schauen, um herauszufinden, was ein Button macht. Alles steht direkt am DOM-Element.

Die wichtigsten HTMX-Attribute

HTMX erweitert HTML um Attribute, die Netzwerkanfragen auslösen und den DOM manipulieren.

  • hx-get, hx-post, hx-put, hx-delete, hx-patch: Definiert die HTTP-Methode und den Endpoint.
  • hx-trigger: Wann soll der Request gefeuert werden? (z.B. click, keyup changed delay:500ms, intersect).
  • hx-target: Wo soll die Server-Antwort im DOM platziert werden? (CSS-Selektor).
  • hx-swap: Wie soll das HTML eingefügt werden? (innerHTML, outerHTML, beforebegin, delete).

Ein minimales Beispiel im Vergleich

Der React-Weg (Junior bis Mid-Level):

function LoadUserButton() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);

  const loadUser = async () => {
    setLoading(true);
    const res = await fetch('/api/user/1');
    const data = await res.json();
    setUser(data);
    setLoading(false);
  };

  return (
    <div>
      <button onClick={loadUser} disabled={loading}>
        {loading ? 'Lade...' : 'User laden'}
      </button>
      {user && <div>{user.name} - {user.email}</div>}
    </div>
  );
}

Der HTMX-Weg:

<div>
  <button 
    hx-get="/components/user/1" 
    hx-target="#user-container" 
    hx-swap="innerHTML"
  >
    User laden
  </button>
  
  <div id="user-container"></div>
</div>
Dieses Dokument bearbeiten