Start / Seminare / Next.js 16 Full-Stack-Entwicklung mit React und TypeScript

Modul

Server und Client Components

6 Kapitel in diesem Modul-Video · Laufzeit

Vertonung mit synthetischer Stimme · Inhalte redaktionell verantwortet · © 2026 HECKER CONSULTING, alle Rechte vorbehalten

Transkript

Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.

Server und Client Components

0:00 Wenn Sie aus diesem Seminar nur ein Modul mitnehmen, sollte es dieses sein. Denn hier liegt der eigentliche Bruch mit dem, was Sie aus React-Projekten kennen. Es geht nicht um eine neue Schreibweise oder ein zusätzliches Werkzeug, sondern um eine umgekehrte Grundannahme: Nicht mehr alles läuft im Browser, sondern zuerst einmal nichts.

0:19 Wer diesen Wechsel vollzieht, findet vieles danach selbstverständlich. Wer ihn übergeht, kämpft in jedem weiteren Modul gegen das Framework. Nehmen Sie sich für dieses Modul also Zeit — und stellen Sie ruhig Fragen, wenn sich etwas verkehrt anfühlt. Das tut es am Anfang für fast alle.

Das neue mentale Modell

0:36 Beginnen wir mit der Grundannahme selbst. Sie ist in einem Satz gesagt und braucht trotzdem eine Weile, bis sie sitzt — weil sie einer Gewohnheit widerspricht, die viele Jahre richtig war. Ich möchte Ihnen deshalb nicht nur sagen, wie es jetzt ist, sondern auch, woher die alte Annahme kam. Denn dann merken Sie selbst, an welchen Stellen Ihr bisheriges Vorgehen nicht mehr passt.

0:58 Im App Router laufen Komponenten standardmäßig auf dem Server. Sie dürfen dort direkt in die Datenbank greifen, Geheimnisse verwenden, Dateien lesen — und sie schicken kein Gramm JavaScript in den Browser. Erst wer Interaktivität braucht, holt eine Komponente ausdrücklich in den Client. Denken Sie an eine Küche und einen Speisesaal.

1:17 Früher haben wir dem Gast Zutaten an den Tisch gestellt und ihn kochen lassen. Heute wird in der Küche gekocht, und an den Tisch kommt das fertige Gericht. Nur das Salzstreuen macht der Gast noch selbst. Die Vorzeichen haben sich umgekehrt, und das ist mehr als eine Formulierung. Früher war alles Browser, und Serverrendering war eine Optimierung, die man nachträglich draufsetzte.

1:40 Heute ist der Server der Normalfall, und jede Client-Komponente ist eine begründungspflichtige Ausnahme. Damit ändert sich auch die Frage, die Sie sich beim Bauen stellen. Sie lautet nicht mehr: Wie bekomme ich die Daten in den Browser? Sie lautet: Muss dieser Teil überhaupt in den Browser? Das ist eine andere Denkbewegung, und sie fällt anfangs schwer, weil die alte so lange richtig war.

2:03 Diese Tabelle ist Ihre Entscheidungshilfe für die nächsten Wochen. Die rechte Spalte hat einen gemeinsamen Nenner: Alles dort braucht entweder den Nutzer oder den Browser — Klicks, Eingaben, Speicher im Gerät, Lebenszyklus. Die linke Spalte hat ebenfalls einen: Alles dort braucht Nähe zu den Daten oder soll gerade nicht im Browser landen.

2:24 Wenn Sie unsicher sind, hilft eine einfache Prüfung: Passiert hier etwas, weil ein Mensch etwas tut? Dann Client. Passiert es, weil die Seite entsteht? Dann Server. Die meisten Fälle sind damit entschieden. Diese drei Punkte lesen sich wie Einschränkungen und sind eigentlich Definitionen. Eine Server-Komponente kann keine Hooks verwenden und keine Klicks entgegennehmen — nicht weil das jemand verboten hätte, sondern weil sie zu dem Zeitpunkt, an dem der Klick passiert, längst nicht mehr existiert.

2:53 Sie hat ihre Ausgabe erzeugt und ist fertig. Dasselbe gilt für den React-Kontext: Er lebt in einem laufenden Baum im Browser, den es hier nicht gibt. Wenn Sie das so lesen, verschwindet das Gefühl der Willkür. Es sind zwei Umgebungen, und jede kann etwas anderes.

Die Grenze mit use client ziehen

3:09 Jetzt zur Praxis: Wie zieht man diese Grenze eigentlich? Die Antwort ist eine einzige Zeile, und genau deshalb wird sie so oft an der falschen Stelle gesetzt. Denn diese Zeile markiert nicht das, was die meisten annehmen. Wer den Unterschied verstanden hat, schreibt fast automatisch schlankere Anwendungen — und wer ihn übersieht, wundert sich über ein Bundle, das immer weiter wächst.

3:31 Hier ist das entscheidende Missverständnis: Die Direktive markiert nicht eine Komponente, sondern einen Einstiegspunkt. Ab dieser Datei ist alles, was von dort importiert wird, Client-Code — der gesamte Teilbaum. Es ist wie eine Tür in einen Sicherheitsbereich: Sie kennzeichnen nicht den Raum dahinter, sondern die Tür. Und alles, was durch diese Tür geht, ist drin.

3:54 Deshalb gehört die Direktive so weit unten im Baum wie möglich. An den Schalter, nicht an die Seite. Sehen wir uns an, was das ausmacht. Was hier zählt, ist nicht der Code, sondern seine Position im Projekt. Diese kleine Komponente ist die einzige, die interaktiv sein muss — sie nimmt eine Eingabe entgegen. Die Liste darüber, die die Daten holt und darstellt, bleibt Server-Komponente und schickt kein JavaScript mit.

4:19 Worauf es ankommt: Diese Trennung entsteht nicht von selbst, Sie müssen sie schneiden. Und sie hat einen Preis in der Struktur — Sie brauchen eine zusätzliche Datei für ein paar Zeilen Code. Das fühlt sich beim Schreiben unnötig an und zahlt sich beim Laden aus. Machen wir es an unserem Belegungsplan konkret. Wenn die Direktive an der Seite steht, wandert alles in den Browser: die Liste, die Formatierung, die Logik zur Darstellung der Belegungszeiträume, womöglich eine Datumsbibliothek.

4:49 Steht sie am Filterknopf, wandert der Filterknopf. Der Unterschied kann ein paar hundert Kilobyte betragen — bei jedem Seitenaufruf, für jeden Nutzer, auch für den mit schlechtem Empfang. Und ein praktischer Hinweis: Sie müssen die Direktive nicht in jede Kindkomponente schreiben. Der Einstiegspunkt genügt, alles darunter ist automatisch Client.

5:09 Der erste Punkt ist formal und kostet trotzdem Zeit: Die Direktive muss ganz oben stehen, über allen Importen. Der zweite ist der begriffliche Kern, den wir gerade besprochen haben. Der dritte ist ein Vorgriff auf das nächste Kapitel und die wichtigste Ausnahme: Was als Kind durchgereicht wird, ist nicht betroffen. Der vierte betrifft fremde Bibliotheken — manche Bündelwerkzeuge entfernen die Direktive beim Veröffentlichen, sodass eine Komponente im Server landet, die dort nicht laufen kann.

5:38 Die Lösung ist simpel: eine eigene kleine Datei mit der Direktive drumherum.

Komposition über die Grenze hinweg

5:43 Jetzt kommt das Muster, das die meisten vermeintlichen Konflikte an dieser Grenze auflöst — und das trotzdem selten genutzt wird, weil man die Regel nur zur Hälfte im Kopf hat. Wenn Sie bisher gedacht haben, dass ein interaktives Element seine gesamte Umgebung in den Browser zieht, wird dieses Kapitel Ihre Architektur verändern.

6:01 Die Regel, die alle kennen: Server darf Client rendern, umgekehrt nicht. Die Hälfte, die oft fehlt: Eine Client-Komponente kann sehr wohl Server-Inhalt enthalten — sie darf ihn nur nicht selbst importieren. Wenn sie ihn als Kind übergeben bekommt, ist er beim Ankommen bereits fertig gerendert. Bildlich gesprochen: Der Bilderrahmen kommt aus dem Browser, das Bild darin aus der Küche.

6:23 Der Rahmen weiß gar nicht, was er umschließt. Deshalb ist es auch kein Problem, dass darin Datenbankzugriffe stattgefunden haben. Sehen Sie sich die beiden Importe oben an — der eine ist Client, der andere Server, und beide stehen friedlich nebeneinander. Der Aufklapper braucht Interaktivität, also lebt er im Browser. Die Saisonstatistik greift auf die Datenbank zu und bleibt, wo sie ist.

6:47 Weil sie als Kind übergeben und nicht importiert wird, wandert von ihr nichts ins Bundle. Worauf es ankommt: Dieses Muster ist der Normalfall und nicht der Sonderfall. Immer wenn Sie denken, ein interaktives Element zwinge Sie, alles darin ebenfalls in den Browser zu verschieben — dann greift es. Der Grund liegt in der Verkürzung: Die Regel wird als Server darf Client im Gedächtnis behalten, und die Gegenrichtung wird als unmöglich abgespeichert.

7:14 Das ist verständlich, aber es kostet. Der entscheidende Unterschied ist nämlich nicht Server und Client, sondern importieren und durchreichen. Nur Importe wandern ins Bundle. Der Kontext-Provider ist übrigens genau derselbe Fall: eine Client-Komponente als Hülle, Server-Inhalt darin. Wenn Sie sich das einmal an einem Beispiel klar gemacht haben, sehen Sie es überall — und bauen Ihre Anwendungen deutlich anders.

7:39 Der zweite Punkt verdient eine Ergänzung, weil er häufig gegen die Gewohnheit geht: Provider gehören so tief wie möglich in den Baum, nicht um das ganze Dokument. Viele setzen sie aus Reflex ganz nach oben, weil das früher richtig war. Damit machen Sie aber alles darunter potenziell zu Client-Code und nehmen Next.js die Möglichkeit, die statischen Teile vorab zu rendern.

8:00 Der dritte Punkt ist der Umgang mit fremden Komponenten: Wenn eine Bibliothek die Direktive nicht mitbringt, packen Sie sie in eine eigene kleine Datei ein. Zwei Zeilen, Problem gelöst.

Was die Grenze passieren darf

8:11 Jetzt wird es ernst. Bisher ging es um Bundle-Größe und Struktur — jetzt geht es um Daten, die im Browser landen, ohne dass es jemand bemerkt. Dieses Kapitel ist kurz, aber es ist der wichtigste Sicherheitsabschnitt der ersten Modulhälfte. Und das Tückische daran: Es funktioniert alles. Es fällt nichts aus, nichts wird rot. Deshalb sehen wir genau hin.

8:34 Werte, die Sie von einer Server- an eine Client-Komponente übergeben, werden serialisiert und mitgeschickt. Funktionen und Klassen sind blockiert, alles andere geht durch. Und genau hier liegt die Gefahr: Wenn Sie einen kompletten Datenbanksatz übergeben, landet er vollständig im Browser — mit allen Feldern, auch denen, die Sie gar nicht darstellen.

8:54 Der Nutzer sieht sie nicht auf der Seite. Er findet sie in zwei Klicks in den Entwicklerwerkzeugen. Nicht gerendert heißt eben nicht: nicht übertragen. Zwei Zeilen, und der Unterschied entscheidet über eine Datenschutzmeldung. Oben wandert der ganze Gast in den Browser: Telefonnummer, Adresse, vielleicht ein interner Vermerk.

9:14 Unten wandern Name und Bootsname. Beide Varianten sehen auf der Seite identisch aus. Worauf es ankommt, ist eine Denkgewohnheit: Übergeben Sie Werte, keine Objekte. Das kostet Sie beim Schreiben ein paar Zeichen mehr und nimmt eine ganze Fehlerklasse heraus. Was nicht übergeben wird, kann nicht ausgelesen werden — so einfach ist die Regel.

9:35 Der dritte Punkt hier ist der, an den niemand denkt. Es geht nicht nur um heute, sondern um den Umbau in einem halben Jahr. Wenn die Signatur breit ist — nimm einen Gast entgegen — dann lädt sie dazu ein, immer mehr hineinzustecken. Und wenn irgendwann ein neues Feld in der Datenbank auftaucht, ist es automatisch dabei. Niemand hat eine Entscheidung getroffen, und trotzdem hat sich etwas geändert.

9:59 Ein Hinweis noch: Die Typprüfung hilft hier nicht. Ein breiter Typ ist für sie vollkommen in Ordnung. Sie müssen die Enge selbst wollen. Der erste Punkt beschreibt, warum das ein wanderndes Ziel ist: Ein Feld, das heute harmlos aussieht, kann morgen personenbezogen sein. Bei uns im Portal wäre das etwa ein interner Vermerk zum Gast, den irgendwann jemand ergänzt.

10:21 Der zweite Punkt ist der Satz, den Sie mitnehmen sollten — nicht gerendert heißt nicht nicht übertragen. Und der dritte nimmt eine falsche Sicherheit: Die Typprüfung beanstandet ein breites Objekt nicht. Sie ist ein hervorragendes Werkzeug, aber nicht für diese Frage. Hier hilft nur die bewusste Entscheidung beim Schneiden der Schnittstelle.

Server-Code sauber halten

10:41 Bleiben wir beim Thema Sicherheit, jetzt aus der anderen Richtung. Bisher ging es um Daten, die versehentlich hinausgehen. Jetzt geht es um Code, der versehentlich im Browser landet — Datenbankzugriffe, Zugangsdaten, interne Logik. Next.js hat dafür zwei Mechanismen, und beide sind angenehm unspektakulär. Man muss nur wissen, dass es sie gibt.

11:04 Der erste Mechanismus ist ein Paket, das nichts tut außer einer Sache: Wenn ein Modul, das es importiert, im Browser landen würde, bricht der Build ab. Es ist wie ein Schild an einer Tür, das nicht nur bittet, sondern verriegelt. Der zweite Mechanismus betrifft Umgebungsvariablen: Die sind von Haus aus serverseitig, und nur ein bestimmtes Namenspräfix schiebt einen Wert in den Browser.

11:26 Wichtig dabei — dieses Präfix ist keine Einstellung, es ist eine Veröffentlichung. Was einmal draußen ist, holen Sie nicht zurück. Eine Zeile, ganz oben in der Datei. Was danach in dieser Datei steht, kann nicht mehr versehentlich im Browser landen — der Versuch scheitert beim Bauen, mit einer klaren Meldung. Worauf es ankommt, ist die Denkweise dahinter: Sie verlassen sich nicht auf Aufmerksamkeit, sondern bauen eine Schranke ein.

11:52 Denn der Fehler, den das verhindert, ist harmlos aussehend — jemand importiert eine Hilfsfunktion aus dieser Datei in eine Client-Komponente, weil sie dort gerade praktisch ist. Ohne diese Zeile geht das durch und nimmt die Zugangsdaten mit. Die dritte Zeile ist die, über die man sprechen muss. Alles mit diesem Präfix steht anschließend im ausgelieferten Bundle — im Klartext, für jeden lesbar, der die Entwicklerwerkzeuge öffnet.

12:17 Das ist völlig in Ordnung für eine Analyse-Kennung oder eine öffentliche Adresse. Es ist katastrophal für einen Schlüssel. Und die unangenehme Eigenschaft: Wenn Sie den Fehler bemerken und die Variable umbenennen, ist sie in allen bereits ausgelieferten Bundles weiterhin enthalten. Der Schlüssel muss also gewechselt werden, nicht nur die Konfiguration.

12:38 Der dritte Punkt ist der heimtückischste, weil er sich wie ein anderer Fehler anfühlt. Wenn Sie eine Variable ohne Präfix im Browser verwenden, wirft Next.js keinen Fehler — es ersetzt den Wert durch eine leere Zeichenkette. Ihre Anwendung läuft also weiter und verhält sich seltsam: Ein Aufruf schlägt fehl, eine Bedingung greift nicht, etwas ist plötzlich leer. Man sucht dann an ganz anderer Stelle.

13:00 Wenn Sie dieses Muster kennen — es funktioniert alles, aber ein Wert ist unerklärlich leer — haben Sie den Fehler in zwei Minuten statt in einer Stunde.

Root Params gegen Prop-Drilling

13:10 Zum Abschluss eine kleine Neuerung aus Version 16.3, die ein sehr konkretes Ärgernis beseitigt. Es geht um Werte, die eigentlich global sind und die man trotzdem durch den halben Komponentenbaum reichen musste. Wer schon einmal eine mehrsprachige Anwendung gebaut hat, weiß sofort, wovon ich spreche. Parameter, die oberhalb des Wurzel-Layouts liegen — die Sprache ist das Standardbeispiel — sind faktisch global.

13:35 Trotzdem musste man sie bisher als Eigenschaft von Komponente zu Komponente weiterreichen, oft durch ein Dutzend Ebenen, von denen keine sich dafür interessierte. Seit 16.3 lassen sie sich direkt lesen, aus jeder Server-Komponente heraus. Das ist ungefähr so, als hätten Sie bisher jeden Brief persönlich zum Nachbarn getragen und bekämen jetzt einen Briefkasten.

13:56 Angenehmer Nebeneffekt: Es funktioniert auch innerhalb gecachter Bereiche. Der Gewinn steht nicht im Code, sondern in dem, was fehlt: die Kette von Eigenschaften, die diesen Wert bis hierher gebracht hätte. Für ein zweisprachiges Hafenmeister-Portal ist das der Unterschied zwischen einer sauberen Struktur und einer Prop, die durch zwölf Komponenten wandert und in jeder davon im Weg steht.

14:18 Worauf es ankommt: Das gilt nur für Parameter oberhalb des Wurzel-Layouts. Gewöhnliche Parameter kommen weiterhin über die Eigenschaften der Seite — und das ist auch richtig so, denn die sind eben nicht global. Alle drei Punkte kreisen um dieselbe Abgrenzung: Das ist ein Werkzeug für einen eng umrissenen Fall, keine allgemeine Abkürzung.

14:38 Wer versucht, damit die Liegeplatznummer aus der Route zu lesen, wird enttäuscht — die ist nicht global, die gehört zu einer bestimmten Seite. Und der zweite Punkt ist eine Momentaufnahme, die sich ändern kann: Derzeit funktioniert das in Server- Komponenten, aber noch nicht in Endpunkten und Aktionen. Der Hersteller hat das angekündigt. Wenn Sie also auf eine Einschränkung stoßen, lohnt ein Blick in die aktuelle Dokumentation.

15:03 In dieser Aufgabe geht es um eine Entscheidung, nicht um Syntax: Welcher Teil Ihrer Ansicht braucht wirklich Interaktivität? Die Antwort ist fast immer kleiner als der erste Reflex. Der Erfolg ist überprüfbar — der Belegungsplan filtert im Browser, die Liste selbst liefert kein JavaScript aus. Und es gibt eine Fortsetzung: In Modul dreizehn messen wir mit der Bundle-Analyse nach, was Ihre Entscheidung tatsächlich gebracht hat.

15:28 Merken Sie sich also, wie Sie geschnitten haben, dann wird die Messung später aussagekräftig.

Modul 4 zusammengefasst

15:34 Vier Sätze zum Mitnehmen. Der Server ist der Standard, der Client die begründete Ausnahme. Die Direktive markiert einen Einstiegspunkt, nicht eine Komponente. Was durchgereicht wird, wandert nicht ins Bundle — deshalb löst das Kind-Muster fast jeden Konflikt. Und schmale Eigenschaften sind Sicherheitsarbeit, nicht Ästhetik.

15:54 Damit ist das Fundament gelegt. Im nächsten Modul wird es praktisch: Wir holen Daten, und Sie werden sehen, wie viel von dem verschwindet, was in Client-Anwendungen an Zeremonie nötig war.

Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →