</>
{ }
React · RSC · Next.js · Streaming · App Router
React Server Components:
der komplette konzeptuelle Guide

React Server Components sind kein neues Rendering-Modell – sie sind ein fundamentaler Paradigmenwechsel. Das Denken in Client-only-Komponenten funktioniert nicht mehr. Wer RSC wirklich versteht, baut Apps, die weniger JavaScript ausliefern, schneller laden und Datenbankzugriffe direkt in Komponenten kapseln können.

20 Min. Lesezeit RSC · Client Components · Streaming · Suspense · App Router · Compositing React 18+ · Next.js 14+ · TypeScript

1. Das neue Paradigma: warum RSC existieren

Das Grundproblem, das React Server Components lösen, lässt sich in einem Satz beschreiben: Zu viel JavaScript wird an den Client ausgeliefert, der es gar nicht braucht. Eine Komponente, die Datenbankdaten rendert und keine Interaktivität hat, muss nicht im Browser ausgeführt werden. Klassisches SSR rendert die Seite auf dem Server zu HTML, schickt aber das vollständige JavaScript-Bundle mit, damit React im Browser „hydratisieren" kann – die gesamte Komponentenlogik landet im Client-Bundle, obwohl viele Komponenten nie im Browser interaktiv werden.

RSC gehen einen anderen Weg: Server Components werden ausschließlich auf dem Server ausgeführt. Ihr Code kommt nie im JavaScript-Bundle an, das an den Browser gesendet wird. Bibliotheken, die nur Server Components nutzen, kosten keinen Bundle-Platz. Datenbankzugriffe, Dateisystem-Operationen und API-Aufrufe können direkt in Komponenten stattfinden – ohne useEffect, ohne useState, ohne Ladezustände für den initialen Datenabruf. Das reduziert die Komplexität erheblich und verbessert die Performance durch kleinere Bundles und schnellere Initial-Render-Zeiten.

2. RSC-Architektur: Server, Client und die Grenze dazwischen

In einer RSC-Architektur gibt es zwei Welten: die Serverwelt und die Clientwelt. Die Grenze zwischen ihnen ist explizit markiert und einseitig durchlässig. Alle Komponenten sind standardmäßig Server Components – sie werden auf dem Server ausgeführt, rendern zu einer serialisierten Komponentenbeschreibung (nicht zu HTML) und werden an den Client übertragen. Client Components müssen mit der Direktive 'use client' explizit markiert werden.

Die Grenze ist einseitig: Server Components können Client Components importieren und als Kinder rendern. Aber Client Components können keine Server Components importieren – das würde bedeuten, Server-only-Code ins Client-Bundle zu ziehen. Es gibt jedoch einen Ausweg: Server Components können als children-Prop an Client Components übergeben werden. Das Client-Component-Element erhält dann die bereits gerenderte RSC-Ausgabe als Prop, ohne den Server-Code selbst zu importieren. Dieses Compositing-Muster ist das Herzstück der RSC-Architektur.


// app/page.tsx — Server Component (default, no directive needed)
// Direct database access — no useEffect, no loading state needed
import { db } from '@/lib/db';
import { ProductCard } from './ProductCard'; // Client Component
import { Suspense } from 'react';
import { ProductSkeleton } from './ProductSkeleton';

// Server Component: runs only on server, never in browser bundle
export default async function ProductsPage() {
  // Direct DB call — no API round-trip, no client bundle cost
  const products = await db.product.findMany({
    where: { active: true },
    orderBy: { createdAt: 'desc' },
    take: 20,
  });

  return (
    <main>
      <h1>Produkte</h1>
      <Suspense fallback={<ProductSkeleton count={20} />}>
        {/* Pass server-fetched data as props to client component */}
        <ProductGrid products={products} />
      </Suspense>
    </main>
  );
}

// app/ProductGrid.tsx — Server Component renders Client Components
async function ProductGrid({ products }: { products: Product[] }) {
  return (
    <div className="grid grid-cols-3 gap-4">
      {products.map((product) => (
        // ProductCard is 'use client' — handles add-to-cart interaction
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

3. Server Components: was sie können und was nicht

Server Components können alles, was auf dem Server möglich ist: Datenbankzugriffe mit ORMs, Dateisystem-Lesen, Backend-APIs direkt aufrufen, Secrets aus Umgebungsvariablen lesen. Sie können asynchron sein – async function ServerComponent() ist ein vollständig unterstütztes Muster. Das bedeutet, Daten können mit await direkt im Component-Body abgerufen werden, ohne useEffect oder useState für den Ladezustand.

Was Server Components nicht können: Sie dürfen keinen Browser-spezifischen State halten (useState, useReducer), keine Effects registrieren (useEffect), keine Event-Handler verwenden (onClick, onChange) und keine Browser-APIs wie window, localStorage oder document nutzen. Der Versuch, diese in einer Server Component zu verwenden, führt zu einem Build-Fehler. Das ist beabsichtigt: Server Components sind pure Render-Maschinen ohne Seiteneffekte auf der Client-Seite.

4. Client Components: use client und seine Bedeutung

'use client' ist keine Aussage über wo eine Komponente ausgeführt wird – es ist eine Grenzmarkierung. Eine Client Component wird sowohl auf dem Server (für initiales SSR-HTML) als auch im Browser ausgeführt. Die Direktive sagt React: „Ab hier beginnt der Client-Graph". Alle Importe einer 'use client'-Datei werden automatisch in den Client-Bundle aufgenommen, auch wenn sie keine eigene 'use client'-Direktive haben.

Das ist eine kritische Konsequenz: Wenn eine Client Component eine große Bibliothek importiert, landet diese vollständig im Bundle. In Server Components würde dieselbe Bibliothek keinen Bundle-Platz kosten. Daher ist die strategische Frage: Welche Teile der App brauchen wirklich Interaktivität? Alles andere sollte Server Component bleiben. Eine gute Faustregel: Die 'use client'-Grenze so tief wie möglich im Komponentenbaum setzen – so bleibt der Großteil der App im Server-Graph und liefert minimales JavaScript.


// Compositing pattern: Server passes pre-rendered RSC as children to Client
// This avoids importing server code into client bundle

// app/Dashboard.tsx — Server Component (no directive)
import { SidebarNav } from './SidebarNav'; // Client Component (needs state)
import { Analytics } from './Analytics';    // Server Component (DB access)

export default async function Dashboard() {
  const stats = await fetchStats(); // Direct server-side call

  // Pass Server Component as children to Client wrapper
  return (
    <SidebarNav>
      {/* Analytics is a Server Component passed as prop — not imported by client */}
      <Analytics stats={stats} />
    </SidebarNav>
  );
}

// app/SidebarNav.tsx — Client Component wrapper
'use client';
import { useState } from 'react';

export function SidebarNav({ children }: { children: React.ReactNode }) {
  const [collapsed, setCollapsed] = useState(false);

  return (
    <div className={`layout ${collapsed ? 'sidebar-collapsed' : ''}`}>
      <nav>
        <button onClick={() => setCollapsed(c => !c)}>Toggle</button>
        {/* Nav items */}
      </nav>
      <main>{children}</main>
      {/* children is pre-rendered RSC output — no server code in bundle */}
    </div>
  );
}

5. Compositing-Muster: Server und Client korrekt verschachteln

Das Compositing-Muster ist die wichtigste Technik für saubere RSC-Architektur. Das grundlegende Muster: Interaktive Wrapper-Komponenten sind Client Components, ihr Inhalt kann Server Components sein, die als children-Prop übergeben werden. Das ermöglicht, dass ein interaktiver Accordion-Wrapper im Client läuft, während sein gesamter Inhalt – Texte, Bilder, Produktdaten – als Server Component gerendert wird und kein Bundle-Gewicht hat.

Ein weiteres wichtiges Compositing-Muster ist das Leaf-Component-Muster: Interaktivität an die Blätter des Komponentenbaums verschieben. Statt eine gesamte Produktseite zur Client Component zu machen, bleibt die Produktseite Server Component. Nur der „In den Warenkorb"-Button ist eine kleine, eigenständige Client Component. Das minimiert den Client-Bundle-Anteil drastisch. Kontext-Provider sind typischerweise Client Components, die ganz oben im Baum stehen, um State für Kind-Client-Components bereitzustellen – können aber Server-Component-Children rendern.

6. Streaming mit Suspense: progressive Renderauslieferung

Streaming in RSC bedeutet, dass der Server nicht auf die langsamste Datenquelle wartet, bevor er mit dem Senden beginnt. Stattdessen sendet er den HTML-Rahmen sofort und streamt Teile der Seite nach, sobald sie fertig sind. Der Browser kann die Seite progressiv aufbauen – der Nutzer sieht sofort eine Shell, und Inhalte erscheinen, sobald ihre Daten verfügbar sind. Das verbessert die wahrgenommene Ladezeit erheblich, auch wenn die Gesamtladezeit gleich bleibt.

Das technische Vehikel dafür ist Suspense. Ein Server Component, der auf Daten wartet, kann in <Suspense fallback={...}> eingehüllt werden. React streamt sofort den Fallback-Inhalt (Skeleton, Spinner) und ersetzt ihn durch den echten Inhalt, sobald der Server Component fertig ist. Mehrere parallele Suspense-Boundaries ermöglichen unabhängiges Streaming verschiedener Seitenbereiche – die Sidebar kann laden während der Header bereits sichtbar ist und der Produktbereich noch wartet.

7. Datenzugriff direkt in Server Components

Einer der transformativsten Aspekte von RSC ist der direkte Datenbankzugriff in Komponenten. Was früher entweder eine API-Route erforderte (HTTP-Request vom Client) oder komplexes SSR-Datenfetching in getServerSideProps war, kann jetzt direkt im Komponenten-Body stehen. Ein ORM wie Prisma oder Drizzle kann direkt importiert werden, die Query wird serverseitig ausgeführt, das Ergebnis wird als Props an Kind-Komponenten weitergegeben.

Das hat tiefe Auswirkungen auf die Architektur: Datenfetching liegt näher an dem Ort, wo die Daten gebraucht werden. Statt Props-Drilling von einem Top-Level-Datenfetch durch viele Ebenen kann jede Server Component ihre eigenen Daten abrufen. React garantiert dabei, dass parallele Fetches gleichzeitig ausgeführt werden – der Promise.all-Ansatz ist bereits eingebaut. Für Caching und Deduplication zwischen mehreren Komponenten, die dieselben Daten brauchen, stellt Next.js die fetch-Memoization bereit.

8. Server Actions: Formulare und Mutationen

Server Actions sind die andere Seite der RSC-Medaille: Wenn Server Components das Lesen vereinfachen, vereinfachen Server Actions das Schreiben. Eine Server Action ist eine Funktion mit der Direktive 'use server', die im Client aufgerufen werden kann, aber auf dem Server ausgeführt wird. Sie kann direkt in ein <form action={...}> eingebunden werden und übernimmt das HTTP-Handling vollständig.

Das Progressive-Enhancement-Prinzip ist in Server Actions eingebaut: Ein Formular mit einer Server Action funktioniert auch ohne JavaScript im Browser, weil es als normales HTML-Formular mit POST-Request funktioniert. Mit JavaScript werden die Daten über einen fetch-ähnlichen Mechanismus übertragen, ohne Seitenreload. Server Actions können validieren, Datenbank-Operationen durchführen und den Next.js-Cache revalidieren – alles auf dem Server, ohne eine API-Route zu schreiben. Das eliminiert eine ganze Kategorie von Boilerplate-Code.

9. RSC vs. klassisches SSR im Vergleich

RSC und klassisches SSR lösen ähnliche Probleme auf fundamental unterschiedliche Weise. Die Unterschiede im Detail entscheiden darüber, welches Modell für welchen Use-Case besser passt.

Aspekt Klassisches SSR (Pages Router) React Server Components (App Router)
Datenfetching getServerSideProps auf Seitenebene Direkt in jeder Komponente async/await
JavaScript-Bundle Alle Komponenten im Bundle Server Components kein Bundle-Anteil
Streaming Nein (alles oder nichts) Ja, mit Suspense-Boundaries
Formular-Handling API-Route + fetch im Client Server Actions direkt in Formularen
Lernkurve Vertraut, eindeutiges Modell Neue Konzepte: Grenze, Compositing

Klassisches SSR bleibt valide für Projekte, die bereits auf dem Pages Router laufen und keine Migration rechtfertigen. RSC lohnt sich besonders für datenintensive Apps, bei denen Bundle-Größe und Time-to-First-Byte kritisch sind – E-Commerce, Content-Plattformen und Dashboards profitieren am stärksten. Der App Router ist nicht ein „besseres SSR", sondern ein anderes mentales Modell, das eine Lernphase erfordert.

Mironsoft

React Server Components · Next.js App Router · Migration · Architektur

Migration auf React Server Components?

Wir planen und implementieren die Migration vom Pages Router auf den App Router – mit korrekter Server/Client-Grenze, Compositing-Muster und Streaming-Architektur für maximale Performance.

RSC-Architektur

Server/Client-Grenze optimal setzen und Compositing-Muster implementieren

Streaming-Setup

Suspense-Boundaries und progressive Renderauslieferung für bessere UX

Migration

Pages Router zu App Router schrittweise migrieren ohne Betriebsunterbrechung

10. Zusammenfassung

React Server Components sind kein inkrementelles Update, sondern ein neues mentales Modell. Server Components laufen ausschließlich auf dem Server, haben Zugang zu Backend-Ressourcen und tragen kein JavaScript-Bundle-Gewicht. Client Components sind die interaktiven Teile, die im Browser laufen und mit 'use client' markiert werden. Die Server/Client-Grenze wird durch Compositing-Muster überbrückt: Server Components übergeben ihre gerenderte Ausgabe als children-Prop an Client-Wrapper.

Streaming mit Suspense liefert Seitenteile progressiv aus sobald ihre Daten bereit sind – ohne auf die langsamste Datenquelle zu warten. Server Actions vereinfachen Mutations und Formular-Handling, indem sie serverseitige Funktionen direkt im Client aufrufbar machen. Das Ergebnis: weniger JavaScript, schnellere Initial-Render-Zeiten, einfacheres Datenfetching und ein klareres Architektur-Modell für komplexe datenintensive React-Apps.

React Server Components — Das Wichtigste auf einen Blick

Server Components

Standardmäßig. Kein Bundle-Anteil, direkter DB-Zugriff, async/await im Body. Kein useState, useEffect oder Event-Handler.

Client Components

'use client' markiert die Grenze. Interaktivität, State, Effects. Grenze so tief wie möglich setzen für minimales Bundle.

Compositing

Server Components als children an Client-Wrapper übergeben. Client importiert nie Server-Code. Leaf-Components für Interaktivität.

Streaming

Suspense-Boundaries ermöglichen progressive Auslieferung. Skeleton sofort sichtbar, Inhalt streamt nach. Parallele Boundaries laufen unabhängig.

11. FAQ: React Server Components

1Server Components vs. SSR?
SSR sendet trotzdem vollständiges JS-Bundle. Server Components kommen nie ins Bundle – kein JavaScript-Gewicht für reine Render-Komponenten.
2useState in Server Component?
Nicht erlaubt. Server Components dürfen keinen Client-State, keine Effects und keine Event-Handler verwenden. Dafür braucht man 'use client'.
3Was bedeutet 'use client'?
Grenzmarkierung für den Client-Graph. Alle Importe dieser Datei landen im Bundle. Grenze so tief wie möglich setzen.
4Client rendert Server Component?
Nicht direkt. Server Components als children-Prop übergeben. Client erhält gerenderte RSC-Ausgabe, importiert keinen Server-Code.
5Was sind Server Actions?
'use server'-Funktionen die im Client aufrufbar sind, auf dem Server laufen. Keine API-Route nötig. Progressive Enhancement eingebaut.
6Was ist Streaming in RSC?
Server sendet Rahmen sofort, streamt Inhalt nach sobald Daten bereit. Suspense-Boundaries steuern Fallback vs. Inhalt. Parallele Boundaries unabhängig.
7DB-Zugriff in Server Components?
Ja. Prisma/Drizzle direkt importieren, Query mit await im Component-Body. Kein API-Layer, kein useEffect. DB-Code nie im Bundle.
8RSC nur mit Next.js?
Nein, RSC ist ein React-Feature. Next.js App Router ist aktuell die ausgereifteste Implementierung. Andere Frameworks folgen.
9Caching mit Server Components?
Next.js: fetch-Memoization, Data Cache, Full Route Cache. revalidatePath und revalidateTag für gezielte Invalidierung nach Mutationen.
10Pages Router zu App Router migrieren?
Inkrementell: app/ parallel zu pages/. Routen schrittweise migrieren. getServerSideProps durch async Server Components. Interaktivität mit 'use client' markieren.