Die Entwicklung von React ist geprägt von großen Meilensteinen, die für die EntwicklerInnen zwar keine Breaking Changes bedeuten, aber stets einen Paradigmenwechsel mit sich bringen. Das wohl populärste Beispiel war die Einführung der Hook-API.
Bis zu diesem Release war es üblich, Komponenten als Klassen zu implementieren, wenn sie über Lifecycle-Methoden oder State verfügen sollten. Lediglich Komponenten für die reine Anzeige wurden als Funktionen umgesetzt. Die Hook-API ermöglichte es, Funktionskomponenten ebenfalls mit State und Lifecycle-Hooks auszustatten, was innerhalb kürzester Zeit zum Niedergang der Klassenkomponenten geführt hat.
Vor einem vergleichbaren Umbruch stehen wir nun erneut mit der Einführung der React Server Components. Dieses Feature bedeutet nichts Geringeres, als dass eine React-Applikation nun nicht mehr nur im Client, sondern auch serverseitig ausgeführt wird. Der Grund hierfür ist, dass die strikte Trennung von Client und Server einige entscheidende Nachteile mit sich bringt.
Der Lebenszyklus einer typischen clientseitigen React-Applikation beginnt auf einem statischen Webserver, der den Quellcode – also das HTML, CSS und JavaScript – an den Browser ausliefert. Dieser stellt zunächst eine weiße Seite dar. Daraufhin baut React die Applikation dynamisch auf, rendert die Komponenten zunächst ohne Daten, lädt diese im nächsten Schritt und stellt sie schließlich dar. Dieser mehrstufige Prozess bedeutet sowohl für BenutzerInnen als auch für Suchmaschinen eine meist vermeidbare Wartezeit.
Mit Lazy Loading und Suspense for Data Fetching hat React in der Vergangenheit Features eingeführt, die die User Experience deutlich verbessern, indem sie die Bundlesize reduzieren und die BenutzerInnen über Ladezustände auf dem Laufenden halten. 
Mit Next.js Client und Server näher zusammenbringen
Next.js greift mit Version 13 dieses Feature auf und bietet Ihnen als EntwicklerIn die Möglichkeit, eine hybride React-Applikation zu entwickeln. Next ist, wie die Versionsnummer schon vermuten lässt, nichts bahnbrechend Neues. Es handelt sich dabei um ein etabliertes Meta-Framework, also ein Framework, das auf einem anderen Framework – in diesem Fall React – aufbaut und es um zusätzliche Features erweitert. Der Fokus von Next liegt darauf, es EntwicklerInnen leicht zu machen, hochperformante React-Applikationen zu entwickeln, die es mit traditionellen Multipage-Applikationen aufnehmen können.
Next organisiert die einzelnen Ansichten einer Applikation in Page-Komponenten und liefert ein Routing-Konzept, mit dessen Hilfe BenutzerInnen in einer Applikation navigieren können. Diese Form des Routings ermöglicht Static Site Generation (SSG) und Server-Side Rendering (SSR).
Seit Version 13 verfügt Next über zwei verschiedene Router. Der neue App Router soll dabei in Zukunft den bisherigen Pages Router ablösen. Standardmäßig behandelt Next alle Komponenten als Server Components.
Die Prinzipien, die Next mit Version 13 einführt, bedeuten einen Paradigmenwechsel in der Entwicklung von React-Applikationen. Die bisher sehr clientorientierte Entwicklung von Komponenten orientiert sich deutlich mehr am Server. Das führt dazu, dass entschieden mehr Aufgaben an den Server delegiert werden, was ganz neue Caching-Möglichkeiten und weitere Optimierungen ermöglicht.
Erste Schritte: Setup einer Next.js-Applikation
Bevor wir uns die Auswirkungen dieser neuen Features auf die tägliche Arbeit von React-EntwicklerInnen ansehen, sollen Sie zunächst erfahren, wie Sie mit der Entwicklung einer Next-Applikation beginnen können und welche Voraussetzungen hierfür erfüllt werden müssen.
Next bietet ein Werkzeug mit dem Namen create-next-app, das analog zu create-react-app nur die Aufgabe erfüllt, eine neue Applikation für Sie zu erzeugen. Sie müssen dieses Paket also nicht auf Ihrem System installieren. Stattdessen können Sie es mit npx bei Bedarf temporär herunterladen lassen und direkt verwenden. Das hat den Vorteil, dass Sie immer mit der neuesten Version arbeiten.
Create-next-app funktioniert auf allen gängigen Systemen, also Windows, Linux und MacOS, und benötigt lediglich ein installiertes Node.js in Version 16.8 oder höher. Bei der Initialisierung einer Applikation stellt Ihnen create-next-app eine Reihe von Fragen, die die Struktur der Applikation und die verwendeten Werkzeuge betreffen:
- What is your project named? Die Antwort bestimmt den Namen des Verzeichnisses, das Next für Sie und Ihre Applikation erzeugt. Außerdem hält das CLI-Werkzeug den Namen in der package.json fest.
- Would you like to add TypeScript with this project? Next funktioniert sowohl mit JavaScript als auch mit TypeScript. Ich empfehle Ihnen jedoch, dabei auf TypeScript zu setzen, da die Entwicklungswerkzeuge Sie hier deutlich besser unterstützen können.
- Would you like to use ESLint with this project? Beantworten Sie diese Frage mit „ja“, setzt Ihnen Next ESLint für die statische Codeanalyse auf.
- Would you like to use Tailwind CSS with this project? Next unterstützt verschiedene Styling-Hilfsmittel. Tailwind ist ein weit verbreitetes Utility-first Framework, das Next für Sie installieren und einbinden kann.
- Would you like to use the `src/ directory` with this project? Normalerweise setzt Next auf eine flache Hierarchie in einer Applikation. Mit dieser Option können Sie ein src-Verzeichnis erzeugen lassen, das den Quellcode der Applikation enthält. Das public-Verzeichnis mit seinen statischen Assets sowie die Konfigurationsdateien verbleiben im Wurzelverzeichnis der Applikation.
- What import alias would you like configured? Mit import aliases können Sie bestimmte Pfade für das Importieren von Dateien in der Projektkonfiguration festlegen, sodass die Import-Statements kürzer werden.
Nachdem Sie alle Fragen beantwortet haben, setzt Create Next App die Applikation für Sie auf, sodass Sie direkt mit der Implementierung starten können. Das Kommando „npm run dev“ startet den Entwicklungsserver. Standardmäßig setzt Next hier aktuell noch auf Webpack. Mit der Option –turbo können Sie jedoch auch den neuen Bundler Turbopack aus dem Hause Vercel verwenden. Turbopack befindet sich momentan noch im Beta-Stadium, bringt aber schon jetzt eine beeindruckende Performance für den Entwicklungsprozess mit sich.
Datei– und Verzeichniskonventionen
Seit Version 13 verfügt Next über zwei Router, den bisherigen Page Router und den neueren App Router. Beide Router funktionieren parallel, was eine Migration in einer bestehenden Applikation erheblich erleichtert. Der App Router hat die höhere Priorität der beiden. Achten Sie darauf, dass Sie nicht in beiden Routern den gleichen Pfad definieren, da Next dies mit einem Build Error quittiert.
Mit dem App Router erzeugen Sie Unterverzeichnisse, die dem URL-Pfad entsprechen. Für den Pfad /users/list legen Sie im app-Verzeichnis ein Verzeichnis mit dem Namen “users” und darin ein Unterverzeichnis “list” an. Pro Verzeichnis können Sie eine Reihe von Dateien mit besonderer Bedeutung anlegen:
- ts: Diese Datei definiert das grundlegende Layout für das Verzeichnis und seine Unterverzeichnisse.
- ts: Das Template erfüllt einen ähnlichen Zweck wie das Layout, mit dem Unterschied, dass eine neue Komponenteninstanz bei jeder Navigation eingehängt wird.
- ts: Die Error-Komponente bildet eine Error-Boundary, mit deren Hilfe Sie Fehler fangen und eine entsprechende Meldung anzeigen können.
- ts: Die Loading-Komponente ist eine Suspense-Boundary, mit der Sie einen Ladezustand für gekapselte Layouts und Seiten anzeigen können.
- not-found.ts: Diese Komponente bildet eine weitere Error-Boundary, jedoch speziell für den Fall, dass ein URL-Pfad keiner konkreten Route zugeordnet werden konnte.
- js: Diese Komponente sorgt für die Darstellung der Inhalte im Browser.
Neben diesen speziellen Next-Komponenten können Sie, wie in React üblich, beliebige eigene Komponenten definieren und in Layouts und Pages einbinden. Sie können diese Komponenten entweder direkt dort ablegen, wo sie benötigt werden, also in der Verzeichnis-Hierarchie des App Routers, oder sollten Sie Komponenten an mehreren Stellen in dieser Hierarchie einbinden, können Sie diese auch in einer separaten Verzeichnishierarchie parallel zum app-Verzeichnis speichern.
Nicht nur bei der Benennung von Dateien, auch bei Verzeichnissen gibt es einige Besonderheiten:
- [name]: Fassen Sie den Verzeichnisnamen in eckige Klammern, steht er für eine Variable. So können Sie mit /users/[id]/page.js beispielsweise auf den Pfad /users/4 reagieren und in der Page-Komponente über die Params auf die Variable zugreifen.
- […name]: Geht es nicht nur um eine, sondern mehrere Variablen, können Sie dem Namen noch drei Punkte voransetzen.
- (name): Mit runden Klammern definieren Sie eine Route Gruppe. Mit deren Hilfe können Sie beispielsweise gemeinsam genutzte Layouts für untergeordnete Pfade definieren. Der Name der Gruppe ist nicht Bestandteil des URL-Pfades.
- @name: Diese Konvention erlaubt parallele Routen. Definieren Sie in einem Pfad auf gleicher Ebene mehrere Verzeichnisse mit @, können Sie auf diese im Layout zugreifen und anzeigen.
Eine einfache Page-Komponente
Eine Next-Applikation setzt sich gewöhnlich aus einer Reihe von Page-Komponenten zusammen. Diese Pages sind, wie auch alle anderen Komponenten, Server Komponenten. Das bedeutet, dass Sie je nach Umgebung, in der Sie Ihre Applikation ausführen, auf den gesamten Funktionsumfang von Node.js, beziehungsweise auf eine eingeschränkte Funktionalität zurückgreifen können, falls Sie die Edge Runtime nutzen. Die Edge Runtime bietet eine stark eingeschränkte Funktionalität der Node.js API und hat einen sehr geringen Ressourcenverbrauch, was für zusätzliche Performance sorgt.
Für die Beispiel-Applikation, einen Marktplatz für lokale Produkte, gehen wir von einer Node.js-Umgebung aus. Die Komponente aus Listing 1 zeigt ein Beispiel für eine Page-Komponente, die Produktdaten serverseitig aus einer asynchronen Funktion bezieht und sie darstellt.
Bei Server Komponenten können Sie auf die Node.js-Funktionalität zurückgreifen und so beispielsweise Informationen aus dem Dateisystem oder von einer Datenbank beziehen. Alternativ dazu können Sie auch die fetch-Funktion nutzen. Hier greift ein weiteres Feature von Next. Das Framework sorgt in diesem Fall dafür, dass Anfragen an die gleiche Ressource lediglich einmal abgesendet werden. Die Antworten können anschließend im Cache vorgehalten werden.
Den Effekt von Next bemerken Sie schon direkt im Browser. Werfen Sie einen Blick in die Antwort des Servers, sehen Sie, dass die HTML-Struktur bereits vorbereitet ist und der Client nur noch die Kontrolle über diese Struktur übernehmen muss. Dieser Prozess wird als Hydration bezeichnet.
Navigation in der Applikation
Next erlaubt es Ihnen, zwischen einzelnen Pages zu navigieren. Zur Demonstration dieses Features implementieren wir im nächsten Schritt eine Detailansicht für die Produkte der Plattform, damit Sie von der Listenansicht zur Detailansicht wechseln können. In Listing 2 sehen Sie den Quellcode der Komponente.
Die Komponente liegt in einer Datei mit dem Namen “page.ts” im Verzeichnis app/details/[id]. Auf die Variable im Pfad können Sie über die Params der Page-Komponente zugreifen und den Wert zum Laden der Daten verwenden. Da es sich bei diesem Pfad um eine dynamische Route handelt, erzeugt Next die Komponente auf Anfrage. Diese Vorgehensweise wird als Server-Side Rendering bezeichnet. Im Gegensatz dazu kann Next die statische Listenkomponente bereits zum Buildzeitpunkt vorbereiten.
Die Architektur der Next-Applikation baut auf den Page-Komponenten auf, orientiert sich also eher an einer Multi Page-Applikation und weniger an den in React üblichen Single Page-Applikationen. Next bietet Ihnen zwei Möglichkeiten zur Navigation. Das ist zum einen die Link-Komponente, die Sie direkt in Ihre JSX-Struktur integrieren können, und zum anderen der useRouter-Hook, den Sie verwenden können, um aus dem TypeScript-Code einer Komponente herauszunavigieren. Dabei gibt es eine Einschränkung: Die Hook-Funktion können Sie nur in einer Client-, nicht aber in einer Server-Komponente verwenden.
Auch, wenn Next seitenbasiert arbeitet, bedeutet das nicht, dass der Browser die Seite bei jeder Navigation komplett neu laden muss. Next optimiert die Navigation, sodass lediglich die Unterschiede geladen werden und die Inhalte ersetzt werden.
Server- vs. Client-Komponenten
Bisher haben Sie nur den Einsatz von Server-Komponenten in Next gesehen. Sie sind der Standard und gewährleisten eine optimale Performance und die Möglichkeit, die Daten direkt auf dem Server zu laden und die Komponente auch dort vorzubereiten.
Server-Komponenten sind jedoch auch Einschränkungen unterworfen. So können Sie nicht auf Interaktionen von BenutzerInnen reagieren. Außerdem können Sie keinen State und keine Lifecycle-Effekte und darauf aufbauende Custom Hooks definieren. Auch der Zugriff auf Browser-APIs bleibt Ihnen verwehrt.
Sie können jedoch Server- und Client-Komponenten miteinander kombinieren. Dabei empfiehlt Next, die Client-Komponenten als Blätter des Komponentenbaums zu implementieren. In der Übersichtsliste können Sie beispielsweise Produkte direkt in den Warenkorb legen. Die Implementierung hierfür finden Sie in Listing 3.
Den Button zum Hinzufügen zum Warenkorb implementieren Sie in der AddToCart-Komponente. Da der Standard für Komponenten in Next Server-Komponenten liegt, können Sie eine Client-Komponente explizit mit „use client“ auszeichnen. Diese Komponente enthält ein Eingabefeld und einen Button. Mit diesen Elementen können Sie die Menge der Artikel festlegen, die Sie zum Warenkorb hinzufügen möchten. Im Click-Handler des Buttons nutzen Sie den Custom Hook useCart. Dieser gewährt Ihnen Zugriff auf den Cart-Kontext und sorgt dafür, dass das Element hinzugefügt wird.
Mit der so vorbereiteten Komponente können Sie aus der Listenansicht heraus Produkte zum Warenkorb hinzufügen. Die Client-Komponenten halten ihren Zustand nur im Browser vor. Das bedeutet, dass Ihr Warenkorb zurückgesetzt wird, sobald Sie die Seite neu laden. Da Next bei der Navigation nur Teile der Ansicht austauscht, können Sie bedenkenlos zwischen der Listen- und Detailansicht hin und her wechseln, ohne dass dies negative Auswirkungen auf den Warenkorb hätte.
Eine Einführung in den neuen App Router – Ausblick
Next ist das erste große Framework, das React-Server-Komponenten der Allgemeinheit zugänglich macht. Dabei leitet es eine neue Ära ein, in der Client und Server wieder näher zusammenrücken und Single Page-Applikationen auch für B2C-Applikationen eine ernstzunehmende Umsetzungsalternative werden.
Next stellt die User Experience und Developer Experience in den Vordergrund, nimmt Ihnen viele alltägliche Aufgaben ab und bietet eine hervorragende Performance.
Im nächsten Artikel zum Thema Next erfahren Sie noch mehr über das Thema Client- und Server-Komponenten, welche Styling-Möglichkeiten Sie haben und wie Sie Ihre Applikation für den Produktivbetrieb bauen können.
Titelmotiv: Photo by Lautaro Andreani on Unsplash