Anfang 2023 gab mir ein Client von mir, ein Fintech-Startup mit Sitz in Canary Wharf, eine Next.js 13-Codebasis, die eine vorherige Agentur gebaut hatte. Sie verwendete das neue app/-Verzeichnis. Schön. Außer dass Hydrationsfehler überall waren, das Bundle war 340 KB gzip, und niemand aus dem alten Team konnte erklären, warum sie „use client" auf buchstäblich jede Datei gesetzt hatten. Als ich sie nach React Server Components fragte, sagten sie: „Ach ja, wir verwenden die." Das taten sie nicht.
Das ist das Problem mit RSCs im Moment. Jeder behauptet, sie zu verstehen. Fast niemand tut das tatsächlich. Also gebe ich dir die Version, die ich mir Anfang 2023 gewünscht hätte.
Was RSCs wirklich sind (nicht die Marketing-Version)
React Server Components sind Komponenten, die nur auf dem Server ausgeführt werden und ihr JavaScript niemals an den Browser versenden. Punkt.
Nicht „serverseitiges Rendern." Nicht „Vorrendern." Diese Dinge existierten vor RSCs und funktionieren anders. Bei traditionellem SSR (was Next.js Pages Router macht) werden deine Komponenten auf dem Server gerendert, um HTML zu erzeugen, aber dann wird dasselbe JavaScript an den Client verschickt, damit React die Seite „hydrieren" kann, Event-Listener anheften und die Kontrolle übernehmen.
RSCs überspringen den zweiten Teil vollständig. Die Komponente läuft auf dem Server, rendert, sendet ihre Ausgabe als serialisierte Payload an den Client – und dann ist Schluss. Für diese Komponente landet nie JavaScript im Browser.
Die praktische Auswirkung: Wenn du eine <ProductDescription> Komponente hast, die einen 40 KB großen Markdown-Parser verwendet, um Text zu rendern, und du machst sie zu einer Server-Komponente, geht diese 40 KB nie zum Nutzer. Das geparste HTML geht zum Nutzer. Fertig.
Das ursprüngliche RFC des React-Teams lohnt sich tatsächlich zu lesen, wenn du die vollständige Begründung möchtest. Es ist dicht, aber ehrlich.
Das mentale Modell, das es mir endlich verständlich gemacht hat
Stell dir deinen Komponenten-Baum als zwei separate Welten vor, die zusammengenäht sind.
Welt 1 (Server). Hat vollen Zugriff auf deine Datenbank, dein Dateisystem, deine Umgebungsvariablen, deine Secrets. Kann useState, useEffect oder Browser-APIs nicht verwenden. Kann keine Event-Listener anhängen.
Welt 2 (Client). Läuft im Browser. Kann alle React-Hooks verwenden, die du kennst. Kann nicht direkt mit deiner Datenbank sprechen. Sendet stattdessen Anfragen an APIs.
Vor RSCs lebte jede Komponente in Welt 2, auch wenn sie zuerst auf dem Server gerendert wurde. Mit RSCs kannst du jetzt explizit Komponenten in Welt 1 platzieren. Diese Komponenten können Welt-2-Komponenten als Kinder rendern und ihnen serialisierbare Props übergeben. Aber Welt-2-Komponenten können Welt-1-Komponenten nicht rendern. Die Grenze ist unidirektional.
Hier verwirren sich die Leute: Du fügst keine Direktive hinzu, um etwas zu einer Server-Komponente zu machen. Im Next.js app/ Verzeichnis sind alle Komponenten standardmäßig Server-Komponenten. Du entscheidest dich für den Client mit „use client". Das Gegenteil von dem, was die meisten erwarten.
Seahawk hatte letztes Jahr ein Projekt – einen großen E-Commerce-Katalog für einen britischen Einzelhändler –, bei dem wir etwa 60 Komponenten überprüft haben. Es stellte sich heraus, dass etwa 40 davon ohne Grund „use client" hatten. Das Entfernen reduzierte das JavaScript-Bundle um etwa 28%. Eine Nachmittagssache.
Was Du tun kannst und was nicht (Eine konkrete Aufschlüsselung)
Server-Komponenten können:
- Daten direkt mit
async/awaitabrufen, keine useEffect, keine Loading-States, einfachconst data = await db.query(...) - Schwere Server-only-Bibliotheken importieren (wie gray-matter für Frontmatter-Parsing oder PDF-Generatoren) ohne das Client-Bundle zu belasten
- Mit Hilfe von Nodes
fsModul aus dem Dateisystem lesen - Auf Umgebungsvariablen zugreifen, die du nicht im Browser exponieren möchtest
- Daten als Props an Client-Komponenten weitergeben
Server-Komponenten können nicht:
useStateoderuseReducerverwendenuseEffectoderuseLayoutEffectverwenden- Event-Handler anhängen (keine onClick, keine
onChange) - Browser-APIs verwenden (window, document,
localStorage) - React Context direkt verwenden (es gibt zwar Muster, um das zu umgehen)
Client Components können alles tun, das Server Components nicht können, aber:
- Sie können nicht direkt auf deine Datenbank zugreifen
- Sie können keine Server-only-Pakete verwenden
- Ihr Code wird zum Browser gesendet
Die Grenzlinie geht nicht nur um Performance. Es geht darum, wo Code läuft und worauf er Zugriff hat. Diese Perspektive ist hilfreicher als in „schnell" vs. „langsam" zu denken.
Data Fetching ist Dort, Wo RSCs Wirklich Glänzen
Das ist der Teil, den ich wirklich liebe. Vor RSCs bedeutete das Abrufen von Daten in einer Next.js-App normalerweise eines von: getServerSideProps , getStaticProps , ein Client-seitiger useEffect-Aufruf oder irgendeine Kombination aller drei mit einem benutzerdefinierten Hook zur Verwaltung des Loading-Status. Unübersichtlich.
Mit RSCs fetchst du einfach... Daten. Innerhalb der Komponente. Auf Top-Level.
`` async function ProductPage({ id }) { const product = await getProduct(id); // calls your DB directly return <ProductDetails product={product} />; } ``
Kein Props Drilling von einer Seiten-Level-Funktion. Keine Loading-Spinner für Daten, die beim Laden hätten bereit sein können. Die Komponente ist asynchron, sie wartet auf das, was sie braucht, und wird gerendert.
Und hier wird es wirklich interessant: Da jede Server-Komponente ihre eigenen Daten unabhängig abrufen kann, vermeidest du das alte „God Component"-Problem, bei dem eine Top-Level-Funktion alle Daten für eine ganze Seite orchestrieren musste. Komponenten werden in sich geschlossen. Paralleles Fetching geschieht auf natürliche Weise, wenn du Promise.all() erwartest oder wenn Geschwisterkomponenten unabhängig abrufen.
Ich habe dieses Muster letzten Frühling bei einem Dashboard-Projekt für ein Logistikunternehmen in Birmingham verwendet. Jedes Widget – Versandstatistiken, Verzögerungsmeldungen, Kostenzusammenfassung – war seine eigene asynchrone Server-Komponente. Kein gemeinsamer Loading-Status, kein Props Drilling, keine Race Conditions. Die Seite ging von einer 2,1-Sekunden-LCP auf 0,9 Sekunden. Nicht nur wegen RSCs, aber RSCs machten die richtige Architektur viel leichter erreichbar.
Die Rendering-Pipeline (Was Tatsächlich Passiert)
Wenn ein Nutzer eine Seite in einer Next.js 14-App mit dem app/ Router anfordert, verläuft die grobe Abfolge wie folgt:
- Next.js führt deine Server Components auf dem Server aus
- Diese Komponenten erzeugen ein spezielles Format namens React Server Component Payload (RSC Payload), nicht rohes HTML, sondern eine serialisierte Beschreibung des UI Tree
- Next.js nutzt diese Payload, um das anfängliche HTML zu generieren (für den ersten Paint)
- Dieses HTML wird zum Browser gesendet
- Die RSC Payload wird ebenfalls gesendet, und die Client-seitige React Runtime nutzt sie, um nur die Client Components im Tree zu hydratisieren
- Client Components erhalten ihr JavaScript, werden hydratisiert und interaktiv
Schritt 5 ist das, was RSCs von reinem SSR unterscheidet. Der Client rendert Server Components nicht neu. Er nutzt die Payload einfach, um das Bild zu vervollständigen, und konzentriert dann die Hydratisierungsanstrengung auf die interaktiven Teile.
Die Next.js-Dokumentation zum Rendering erklärt das Payload-Format besser als die meisten Blog-Posts, die ich gelesen habe, wenn du tiefer gehen möchtest.
Wo RSCs Zusammenbrechen (Ehrliche Kritik)
Okay. Lass uns nicht so tun, als wäre das alles Sonnenschein.
Die `"use client"`-Grenze kaskadiert. In dem Moment, wo du „use client" auf einer Komponente platzierst, wird jede Komponente, die sie importiert, ebenfalls Client-seitig. Wenn du nicht aufpasst, kann ein onClick Handler einen überraschend großen Subtree in das Client Bundle ziehen. Ich habe das bei jüngeren Entwicklern im Team mehrmals erlebt.
Context ist schmerzhaft. React Context funktioniert nicht in Server Components. Wenn du deine App-Architektur rund um Context für Theming, Auth-State oder globale Konfiguration aufgebaut hast, musst du umstrukturieren. Es gibt Workarounds (Werte als Props weitergeben, einen Client Wrapper nutzen), aber das ist Reibung, die es vorher nicht gab.
Debugging ist schwieriger. Server Component Fehler erscheinen nicht auf die gleiche Weise in der Browser-Konsole. Das Error Boundary Verhalten ist anders. Du musst Server Logs überprüfen. Kein Dealbreaker, aber wenn du gewöhnt bist, dass alle Fehler in DevTools landen, erwarte eine Lernkurve.
Third-Party Bibliotheken sind oft nicht bereit. Jede Bibliothek, die unter der Haube Hooks oder Browser APIs nutzt, bricht in einem Server Component zusammen. Du wirst Zeit damit verbringen, Dinge in "use client" Dateien zu wrappen, nur um einen Date Picker oder eine Animation Library zu nutzen. Das React Ökosystem holt auf, aber ab 2024 ist es immer noch lückenhaft.
Ehrlich gesagt brauchst du RSCs für eine einfache Marketing-Website oder einen Content Blog vielleicht gar nicht. Wenn dein Team sich mit dem Pages Router wohlfühlt und deine Performance stimmt, ist ein Upgrade nur für RSC Support allein die Mühe nicht wert. Ich habe Kunden mehr als einmal von einer Migration abgehalten.
Praktische Tipps zur Einführung von RSCs in einem bestehenden Projekt
Wenn du eine bestehende Next.js App in das app/ Verzeichnis migrierst, folge ich dieser Reihenfolge:
- Beginne mit Leaf Components. Komponenten, die Daten anzeigen, aber keine Interaktion verarbeiten, sind die einfachsten Gewinne. Mache sie zunächst zu Server Components.
- Identifiziere deine echte Interaktivitätsoberfläche. Normalerweise ist sie kleiner, als du denkst. Formulare, Modals, Dropdowns. Das ist dein
"use client"Gebiet. - Push `"use client"` so tief wie möglich. Der Button, der ein Formular sendet, sollte ein Client Component sein. Das Formular-Layout um ihn herum muss es wahrscheinlich nicht sein.
- Überprüfe dein Bundle mit [@next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer). Führe es vorher und nachher aus. Wenn du keine sinnvolle Bundle-Reduktion siehst, schiffst du wahrscheinlich immer noch zu viel zum Client.
- Teile keine Server-only Module. Installiere server-only von npm und importiere es am Anfang jedes Moduls, das niemals im Browser landen sollte. Es wird bei der Build-Zeit werfen, wenn etwas versucht, es auf der Client-Seite zu importieren.
Das server-only Paket ist eine kleine Sache, aber es hat uns vor einem ziemlich peinlichen Datenleck-Szenario bei einem Healthcare-Client-Projekt bewahrt. Ein Entwickler hat versehentlich ein Database-Utility in eine Komponente importiert, die später mit "use client" gekennzeichnet wurde. Das Paket hat es bei der Build-Zeit gefangen. Nutze es.
FAQ
Sind React Server Components das gleiche wie SSR?
Nein. SSR (Server-Side Rendering) rendert Komponenten auf dem Server zu HTML, schifft aber das Komponenten-JavaScript immer noch zum Client zur Hydration. RSCs rendern auf dem Server und senden ihr JavaScript niemals zum Browser. SSR und RSCs können zusammenarbeiten, und im Next.js app/ Verzeichnis tun sie das, aber sie lösen unterschiedliche Probleme.
Brauche ich Next.js, um React Server Components zu nutzen?
Technisch nein, aber praktisch ja für die meisten Teams. RSCs erfordern ein Framework, das die Server-Infrastruktur, das Routing und die RSC Payload Pipeline verwaltet. Next.js 13+ mit dem app/ Verzeichnis ist die reifste Option im Moment. Remix hat ein anderes Modell. Dein eigenes Setup zu bauen ist möglich, aber keine gute Zeitnutzung, es sei denn, du baust selbst ein Framework.
Kann ich Server und Client Components auf der gleichen Seite mischen?
Ja, und das ist der ganze Sinn. Eine Seite könnte ein Server Component sein (holt Daten, kein JS verschifft), ein Client Component für ein Sucheingabe-Feld enthalten, das Server Components als Kinder via children Prop enthält. Der Baum verschachtelt sich. Denk nur daran: Server Components können Client Components rendern, aber Client Components können Server Components nicht direkt rendern.
Was passiert mit meinen bestehenden Custom Hooks?
Custom Hooks, die useState, useEffect oder Browser APIs nutzen, können nur in Client Components laufen. Du musst sie nicht umschreiben, sei nur bewusst, welche Komponenten sie nutzen. Wenn ein Hook einen Data-Fetching Call wrappen, überlege, ob diese Daten stattdessen in einem Server Component geholt und als Props übergeben werden könnten. Oft können sie, und du endest mit simplerer Code.
Gibt es Performance-Kosten für das RSC Payload Format?
Es kann sein, besonders wenn du große Datenmengen durch die Payload passierst. Das RSC Payload ist nicht das gleiche wie JSON, es ist ein Custom Format, aber es addiert immer noch Bytes zu deiner Response. Für die meisten Apps ist das zu vernachlässigen im Vergleich zu den JS Bundle Einsparungen. Für Apps mit sehr großen Datensätzen, die als Props übergeben werden, profilere es. Gehe nicht davon aus, dass RSCs immer kleiner über das Netz sind.
---
RSCs sind ein echtes Architektur-Shift, nicht nur eine neue API. Das mentale Modell braucht eine Weile zum Setzen. Gib ihm diese Zeit. Baue etwas Kleines mit dem app/ Verzeichnis, bevor du ein großes Client-Projekt daran bindest. Ich habe etwa drei Wochen für Experimente verbracht, bevor ich mir selbst traute, RSC-basierte Architektur in Produktion zu verschiffen, und ich schreibe React seit 2016.
Das Handwaving wird von Leuten weitergehen, die damit nichts Echtes gebaut haben. Jetzt kennst du zumindest genug, um den Unterschied zu erkennen.
