Streaming SSR im Detail verstehen: Suspense, Selective Hydration & TTFB
AI generated
</>
{ }
React · Suspense · Streaming SSR · Performance
Streaming SSR im Detail verstehen
Suspense, Selective Hydration und Time to First Byte

Streaming SSR zerlegt eine Serverantwort in mehrere Chunks, statt auf das vollständige HTML zu warten, bevor überhaupt etwas an den Browser geschickt wird. React Suspense definiert dabei die Grenzen, an denen einzelne Teile der Seite nachgeliefert werden, während schnell verfügbare Inhalte sofort sichtbar sind.

19 Min. Lesezeit Suspense · loading.tsx · Selective Hydration React 19 · Next.js App Router

1. Was ist Streaming SSR und warum lohnt es sich

Streaming SSR löst ein Grundproblem des klassischen serverseitigen Renderings: Bei traditionellem SSR muss der Server das komplette HTML-Dokument fertig aufgebaut haben, bevor auch nur ein Byte an den Browser geschickt wird. Wenn eine einzelne Datenabfrage langsam ist, etwa eine externe API oder eine komplexe Datenbankabfrage, blockiert diese eine Abfrage die gesamte Seite, selbst wenn neunzig Prozent des Inhalts längst bereitstehen. Streaming SSR durchbricht dieses Alles-oder-nichts-Prinzip, indem der Server das HTML in mehreren Chunks über eine offene HTTP-Verbindung ausliefert, sobald jeder Teil fertig ist.

Der wahrgenommene Geschwindigkeitsgewinn von Streaming SSR ist erheblich, weil der Nutzer den oberen Teil einer Seite, etwa Header und Hero-Bereich, sofort sieht, während im Hintergrund noch Produktempfehlungen oder Kommentare geladen werden. Das Ergebnis ist eine Seite, die sich schneller anfühlt, selbst wenn die Gesamtzeit bis zum vollständigen Laden identisch bleibt. Genau dieser Effekt macht Streaming SSR zu einem der wirkungsvollsten Werkzeuge, um wahrgenommene Performance zu verbessern, ohne an der eigentlichen Datenlast etwas zu ändern.

Technisch basiert Streaming SSR in React auf der Fähigkeit, einen Response-Stream schrittweise zu schreiben, statt einen fertigen String zurückzugeben. Next.js baut mit dem App Router direkt auf diesem Modell auf: Jede Route kann eigene Suspense-Grenzen definieren, an denen die Serverantwort segmentiert wird. Das nächste Kapitel zeigt, wie React Suspense diese Segmentierung technisch ermöglicht.

2. React Suspense als Fundament von Streaming SSR

Ohne <Suspense>-Grenzen gibt es kein Streaming SSR im React-Ökosystem. Eine Suspense-Komponente markiert einen Bereich im Baum, dessen Rendering pausieren darf, während eine asynchrone Operation, meist ein Datenabruf, noch läuft. Statt das gesamte Rendering zu blockieren, zeigt React sofort den fallback-Inhalt der Suspense-Grenze und liefert den eigentlichen Inhalt nach, sobald die Daten verfügbar sind, als separaten HTML-Chunk mit passendem Skript zum Austausch im DOM.

Diese Fallback-dann-Ersetzen-Logik ist der Kern von Streaming SSR. Mehrere Suspense-Grenzen können parallel und unabhängig voneinander auflösen, was bedeutet, dass eine langsame Komponente nicht auf eine schnellere wartet und umgekehrt. React sendet die Chunks in der Reihenfolge, in der die Daten tatsächlich verfügbar werden, nicht in der Reihenfolge, in der sie im JSX-Baum stehen. Diese Eigenschaft von Streaming SSR unterscheidet es fundamental von klassischem SSR, bei dem die Reihenfolge im Code exakt der Auslieferungsreihenfolge entspricht.


// app/dashboard/page.tsx — independent Suspense boundaries stream separately
import { Suspense } from 'react';
import { UserProfile } from './user-profile';
import { RecentOrders } from './recent-orders';
import { Recommendations } from './recommendations';

export default function DashboardPage() {
  return (
    <div>
      {/* Fast: resolves almost immediately, arrives in the first chunk */}
      <Suspense fallback={<ProfileSkeleton />}>
        <UserProfile />
      </Suspense>

      {/* Medium: independent boundary, streams whenever its data resolves */}
      <Suspense fallback={<OrdersSkeleton />}>
        <RecentOrders />
      </Suspense>

      {/* Slow: does not block the two boundaries above */}
      <Suspense fallback={<RecommendationsSkeleton />}>
        <Recommendations />
      </Suspense>
    </div>
  );
}

Ein wichtiges Detail bei Streaming SSR mit Suspense: Der fallback muss synchron und ohne eigene Datenabhängigkeit rendern können, sonst kann React ihn nicht sofort ausliefern. Skeleton-Komponenten, die nur statisches Markup mit Tailwind-Klassen für Platzhalter-Balken enthalten, sind der Standardfall. Eine verschachtelte Suspense-Grenze innerhalb eines Fallbacks funktioniert zwar technisch, verkompliziert aber die mentale Nachvollziehbarkeit des Streamings erheblich und sollte nur gezielt eingesetzt werden.

3. loading.tsx und Route-Level Streaming in Next.js

Next.js abstrahiert die manuelle Suspense-Verkabelung auf Routenebene mit der Konvention loading.tsx. Legt man diese Datei neben eine page.tsx, umschließt Next.js die gesamte Seite automatisch mit einer Suspense-Grenze, deren Fallback der Inhalt von loading.tsx ist. Das ist die einfachste Form von Streaming SSR in Next.js: Der Layout-Rahmen und die Navigation erscheinen sofort, während die eigentliche Seite noch lädt, ganz ohne manuellen Suspense-Import in der Seite selbst.

Für feingranulareres Streaming SSR innerhalb einer Route reicht die automatische loading.tsx-Grenze aber nicht aus, weil sie die gesamte Seite als einen Block behandelt. Sobald einzelne Bereiche einer Seite unabhängig voneinander streamen sollen, etwa ein schneller Seitenkopf und eine langsame Datentabelle, sind explizite <Suspense>-Komponenten in der Server Component selbst nötig, wie im vorherigen Codebeispiel gezeigt. loading.tsx und manuelle Suspense-Grenzen schließen sich nicht aus, sondern ergänzen sich: die Route-Ebene für den groben Fall, einzelne Suspense-Grenzen für die Feinsteuerung.


// app/products/[id]/loading.tsx — route-level streaming fallback
export default function Loading() {
  return (
    <div className="animate-pulse space-y-4 p-6">
      <div className="h-8 bg-slate-200 rounded w-1/3" />
      <div className="h-64 bg-slate-200 rounded" />
      <div className="h-4 bg-slate-200 rounded w-2/3" />
    </div>
  );
}

4. Selective Hydration: Interaktivität vor vollständigem Laden

Streaming allein liefert nur HTML aus, macht die Seite aber noch nicht interaktiv. Selective Hydration ist der Mechanismus, der Streaming SSR im Client fortsetzt: React hydratisiert bereits ausgelieferte Chunks, sobald deren JavaScript geladen ist, unabhängig davon, ob andere Suspense-Grenzen noch auf Server-Daten warten. Das bedeutet, ein Nutzer kann bereits mit dem Header interagieren, während im unteren Seitenbereich noch Inhalte nachgeladen werden.

Besonders wirkungsvoll wird Selective Hydration in Kombination mit React 19s Priorisierung von Interaktionen: Klickt ein Nutzer auf ein Element, dessen Hydration noch nicht abgeschlossen ist, priorisiert React diese Hydration gegenüber anderen, noch unsichtbaren Bereichen. Diese Priorisierung geschieht automatisch und ist einer der Hauptgründe, warum Streaming SSR zusammen mit React 19 spürbar reaktionsfreudiger wirkt als ältere SSR-Implementierungen, bei denen Hydration immer als ein einziger, blockierender Schritt ablief.

Ein Nebeneffekt, den Teams oft übersehen: Weil einzelne Bereiche unabhängig hydratisieren, muss jede Suspense-Grenze in sich selbst funktionsfähig sein, ohne stillschweigend von der Hydration eines Geschwister-Elements abzuhängen. Globaler State, der über Context geteilt wird, funktioniert weiterhin, aber Annahmen über eine garantierte Hydration-Reihenfolge zwischen unabhängigen Suspense-Grenzen sind bei Streaming SSR nicht mehr gültig.

5. Streaming SSR mit renderToPipeableStream im Detail

Unterhalb von Next.js nutzt React für Node.js-Umgebungen die Funktion renderToPipeableStream aus react-dom/server, das eigentliche Low-Level-API hinter Streaming SSR. Diese Funktion gibt ein Objekt mit einer pipe-Methode zurück, die den React-Baum direkt in einen Node-Response-Stream schreibt, Chunk für Chunk, sobald Suspense-Grenzen auflösen. Wer eine eigene SSR-Infrastruktur ohne Next.js betreibt, etwa in einem custom Express-Server, arbeitet direkt mit dieser API.

Zwei Callbacks sind für Streaming SSR mit renderToPipeableStream entscheidend: onShellReady feuert, sobald das initiale HTML-Gerüst, die sogenannte Shell, bereit ist und an den Client gestreamt werden kann, noch bevor alle Suspense-Grenzen aufgelöst sind. onAllReady hingegen wartet, bis wirklich der komplette Baum fertig ist, was für Suchmaschinen-Crawler ohne JavaScript-Ausführung oder für statische Exporte relevant sein kann, wo ein vollständiges Dokument nötig ist statt eines Streams.


// server.js — custom Node server using renderToPipeableStream directly
import { renderToPipeableStream } from 'react-dom/server';
import App from './App';

function handleRequest(req, res) {
  const { pipe, abort } = renderToPipeableStream(<App url={req.url} />, {
    bootstrapScripts: ['/client.js'],
    onShellReady() {
      // Shell is ready — start streaming immediately
      res.statusCode = 200;
      res.setHeader('Content-Type', 'text/html');
      pipe(res);
    },
    onShellError(error) {
      // Shell itself failed — fall back to a static error page
      res.statusCode = 500;
      res.send('<h1>Something went wrong</h1>');
    },
    onError(error) {
      console.error('Streaming error:', error);
    },
  });

  // Abort streaming after 10s to avoid hanging connections
  setTimeout(() => abort(), 10000);
}

6. Fehlerbehandlung: error.tsx und Streaming Boundaries

Fehlerbehandlung bei Streaming SSR ist komplexer als bei klassischem SSR, weil ein Fehler auftreten kann, nachdem bereits Chunks an den Client geschickt wurden und der HTTP-Statuscode 200 längst gesendet ist. Next.js löst das mit der error.tsx-Konvention: Schlägt eine Komponente innerhalb einer Suspense-Grenze fehl, rendert Next.js automatisch die nächstgelegene error.tsx als Ersatz für genau diesen Chunk, ohne die bereits ausgelieferten Teile der Seite zu beeinträchtigen.

Dieses Verhalten ist ein direkter Vorteil von Streaming SSR gegenüber traditionellem SSR: Ein Fehler in einer einzelnen Komponente, etwa einem fehlerhaften Drittanbieter-Widget, reißt nicht die ganze Seite mit sich, sondern nur den betroffenen Suspense-Bereich. Wichtig ist, error.tsx als Client Component zu deklarieren, weil React Error Boundaries, auf denen diese Konvention aufbaut, nur als Client Components funktionieren, ein häufiger Stolperstein für Teams, die neu mit dem App Router arbeiten.

7. Streaming SSR und Time to First Byte messen

Time to First Byte, kurz TTFB, misst die Zeit bis zum ersten empfangenen Byte der Antwort und ist die Metrik, die Streaming SSR am direktesten beeinflusst. Bei klassischem SSR entspricht TTFB praktisch der Gesamtrenderzeit des Servers, weil nichts geschickt wird, bevor alles fertig ist. Bei Streaming SSR sinkt TTFB drastisch, weil die Shell bereits gestreamt wird, sobald sie bereit ist, während einzelne Suspense-Inhalte erst später folgen.

Wichtig für korrekte Interpretation: TTFB allein sagt bei Streaming SSR weniger aus als bei klassischem SSR, weil eine schnelle Shell nicht automatisch bedeutet, dass die Seite für den Nutzer vollständig nutzbar ist. Ergänzend sollten Largest Contentful Paint und Time to Interactive gemessen werden, idealerweise mit echten Nutzerdaten über das Chrome User Experience Report Programm, statt sich ausschließlich auf synthetische Labor-Messungen zu verlassen, die die tatsächliche Netzwerklatenz zum Nutzer nicht abbilden.

8. Grenzen von Streaming SSR: wann es nicht hilft

Streaming SSR verbessert die wahrgenommene Ladezeit, ändert aber nichts an der tatsächlichen Serverlast oder der Gesamtmenge an Daten, die transportiert werden muss. Eine Anwendung, deren Datenbankabfragen grundsätzlich zu langsam sind, profitiert von Streaming nur bedingt: Die Shell erscheint schnell, aber der Nutzer wartet trotzdem auf die eigentlich relevanten Inhalte, nur eben mit einem Skeleton statt einem leeren Bildschirm.

Für Suchmaschinen-Crawler, die kein JavaScript ausführen und nicht auf nachgelieferte Chunks warten, kann Streaming SSR problematisch sein, wenn wichtiger Content erst in einer späten Suspense-Grenze landet. Zwar liefert React auch für Bots letztlich den vollständigen Baum aus, moderne Crawler wie der Googlebot warten inzwischen auf abgeschlossenes Rendering, aber bei zeitkritischen oder ressourcenlimitierten Crawlern ist das kein Standard. Kritische SEO-relevante Inhalte sollten deshalb möglichst in der Shell selbst liegen, nicht in einer spät auflösenden Suspense-Grenze.

9. Streaming SSR im Vergleich

Um Streaming SSR richtig einzuordnen, hilft der direkte Vergleich mit klassischem SSR, reinem Client-Side Rendering und statischer Generierung.

Rendering-Modell TTFB Blockiert bei langsamen Daten Geeignet für
Klassisches SSR Hoch bei langsamen Queries Ja, gesamte Seite Einfache, schnelle Datenquellen
Streaming SSR Niedrig, Shell sofort Nein, nur einzelne Suspense-Bereiche Gemischt schnelle/langsame Datenquellen
Client-Side Rendering Sehr niedrig Nein, aber leerer Bildschirm zuerst Hoch interaktive Dashboards ohne SEO-Fokus
Statische Generierung Minimal, CDN-Auslieferung Nein, vorab gebaut Selten ändernde, cachebare Inhalte

Die Tabelle zeigt, dass Streaming SSR zwischen klassischem SSR und Client-Side Rendering steht: Es behält die SEO- und Sicherheitsvorteile von serverseitigem Rendering, verbessert aber die wahrgenommene Ladezeit deutlich gegenüber der Alles-oder-nichts-Auslieferung des klassischen Modells.

Mironsoft

React Performance, SSR-Architektur und Core Web Vitals

Seiten, die sofort etwas anzeigen statt leer zu bleiben?

Wir bauen Streaming SSR mit sauber geschnittenen Suspense-Grenzen, robuster Fehlerbehandlung und messbarer TTFB-Verbesserung, ohne SEO-relevante Inhalte in späten Chunks zu verlieren.

Suspense-Architektur

Grenzen so schneiden, dass schnelle Inhalte sofort sichtbar sind

Performance-Messung

TTFB, LCP und Time to Interactive mit echten Nutzerdaten auswerten

Fehler-Isolation

error.tsx Grenzen so setzen, dass Ausfälle isoliert bleiben

10. Zusammenfassung

Streaming SSR löst das Alles-oder-nichts-Problem von klassischem serverseitigem Rendering, indem React Suspense den Baum in unabhängig auflösende Chunks segmentiert. Die Shell erscheint sofort, langsamere Bereiche folgen, sobald ihre Daten bereitstehen, ohne dass schnelle Inhalte auf langsame warten müssen. Next.js macht diese Technik mit loading.tsx auf Routenebene zugänglich und erlaubt feinere Kontrolle über manuelle Suspense-Grenzen in Server Components.

Selective Hydration setzt dieses Prinzip auf dem Client fort, indem interaktive Bereiche unabhängig voneinander hydratisiert werden, priorisiert nach tatsächlicher Nutzerinteraktion. Die größten Grenzen von Streaming SSR liegen nicht in der Technik selbst, sondern darin, dass es die zugrunde liegende Datenlast nicht reduziert und SEO-kritische Inhalte in einer frühen Suspense-Grenze platziert werden müssen, um zuverlässig indexiert zu werden.

Streaming SSR: Das Wichtigste auf einen Blick

Suspense-Grenzen

Segmentieren den Rendering-Baum in unabhängig auflösende Chunks, die in Fertigstellungsreihenfolge gestreamt werden.

loading.tsx

Automatische Routen-weite Suspense-Grenze in Next.js, ergänzbar durch manuelle Suspense-Komponenten für Feinsteuerung.

Selective Hydration

Hydratisiert unabhängige Bereiche einzeln, priorisiert nach echter Nutzerinteraktion statt starrer Reihenfolge.

Grenzen kennen

Reduziert nicht die Datenlast. SEO-kritische Inhalte gehören in die Shell, nicht in eine spät auflösende Grenze.

11. FAQ: Streaming SSR

1Unterschied zu klassischem SSR?
Klassisches SSR liefert erst nach vollständigem Rendering, Streaming SSR sendet Chunks sobald sie fertig sind.
2Warum braucht es Suspense?
Suspense markiert Bereiche, die pausieren dürfen, und liefert damit die Grenzen für die Chunk-Aufteilung.
3Was macht loading.tsx?
Umschließt die Seite automatisch mit einer Suspense-Grenze, zeigt seinen Inhalt als Fallback beim Laden.
4Was ist Selective Hydration?
Unabhängige Hydration einzelner Bereiche, priorisiert nach tatsächlicher Nutzerinteraktion.
5Wofür renderToPipeableStream?
Low-Level React-API für Node, schreibt den Baum direkt in einen Response-Stream. Next.js nutzt sie intern.
6Fehlerbehandlung beim Streaming?
error.tsx ersetzt nur den betroffenen Chunk, bereits ausgelieferte Teile der Seite bleiben unberührt.
7Immer bessere TTFB?
Ja, aber TTFB sollte zusammen mit LCP und Time to Interactive betrachtet werden, nicht isoliert.
8Gut für SEO?
Grundsätzlich ja, kritische Inhalte trotzdem in der Shell platzieren, nicht in später auflösenden Grenzen.
9Reduziert es die Serverlast?
Nein, verbessert nur die wahrgenommene Ladezeit, nicht die tatsächliche Rechenlast.
10Manuelle Konfiguration nötig?
Oft reicht loading.tsx oder eine Suspense-Komponente, für feinere Kontrolle zusätzliche manuelle Grenzen setzen.