
Fullstack React: Das ist neu und wichtig
Ein Überblick der Begriffe, Konzepte und Ideen
Von: Nils Hartmann
Das React-Team hat die Empfehlung ausgesprochen, React-Anwendungen künftig nur noch mit einem Fullstack-Framework zu entwickeln. Seitdem ist eine leidenschaftliche Diskussion über die Fürs und Widers dieser Entscheidung im Gang. Außerdem schwirren viele neue Begriffe, Konzepte und Ideen durch das React-Ökosystem. Dieser Artikel soll eine Orientierungshilfe bieten.
Klassisch werden React-Anwendungen als Single-Page-Anwendung (SPA) gebaut. Dabei wird der JavaScript-Code der Anwendung in den Browser geladen und dort ausgeführt. Der Server dient lediglich dazu, den JavaScript-Code auszuliefern – wie er es auch mit anderen statischen Assets wie CSS- oder Bild-Dateien verfährt. Sobald der JavaScript-Code vom Browser geladen und ausgeführt wurde, ist die Anwendung interaktiv und kann vom Benutzer bedient werden. Das läuft in der Regel sehr flüssig, weil sämtliche Interaktionen auf dem Client stattfinden und keine Server-Requests zum Neurendern der Darstellung notwendig sind.
Die Schattenseite ist, dass die Anwendung erst dargestellt werden kann, wenn der notwendige JavaScript-Code geladen wurde. Dieser muss dann noch geparst und ausgeführt werden. Das dauert naturgemäß länger, als wenn der Browser fertigen HTML-Code vom Server bekommt und diesen darstellt. Gerade für Anwendungen bzw. Websites, bei denen es auf eine schnelle erste Darstellung ankommt, zum Beispiel Landing- oder Produkt-Seiten, können SPAs zu langsam sein. Um dieses Problem zu lösen, kann man Seiten einer Anwendung mit Server-Side Rendering (SSR) auf dem Server vorrendern. In diesem Fall läuft auf dem Server ein JavaScript-Prozess, der den Request entgegennimmt, dort React verwendet um eine Seite zu rendern und das Ergebnis als fertigen HTML-Code zum Browser schickt. Der Browser kann die HTML-Seite dann unmittelbar anzeigen.
Aber auch bei diesem Ansatz lädt der Browser nach der initialen Darstellung den JavaScript-Code der Anwendung und führt diesen aus, damit die Anwendung interaktiv wird. Dieser Vorgang wird auch als Hydration bezeichnet. Das Laden und Ausführen des JavaScript-Codes ist bei einigen Seiten (oder Teilen von Seiten) aber nicht unbedingt erforderlich. JavaScript wird im Browser nämlich grundsätzlich nur für die interaktiven Teile einer Anwendung benötigt, um zum Beispiel auf Benutzereingaben reagieren zu können. Für statischen Content, der auf dem Server gerendert worden ist, wäre kein JavaScript erforderlich.
An diesem Punkt setzen Fullstack-Frameworks wie Gatsby, Next.js oder Remix an. Da diese Frameworks einerseits zwar React verwenden, andererseits darüber hinaus auch weitere Bibliotheken und Frameworks (etwa für Build, Bundling und Routing) integrieren, werden sie auch als Meta-Frameworks bezeichnet.
Ähnlich wie beim serverseitigen Rendern schicken auch diese Frameworks eine fertige Darstellung einer Seite an den Browser. Allerdings wird hierbei der JavaScript-Code für statische Komponenten nicht zum Browser übertragen, da er dort nicht benötigt wird. Der Browser muss bei diesen Anwendungen also, wie bei Single-Page-Anwendungen, einmalig den JavaScript-Code für das Framework runterladen. Danach wird aber nur noch der JavaScript-Code für jene Komponenten benötigt, die auf dem Client auch interaktiv sein sollen, und nicht mehr für jede Komponente wie bei Single-Page-Anwendungen. Dieser Ansatz wird als Partial Hydration bezeichnet.
Und Partial Hydration geht noch weiter: Nach einer Interaktion, zum Beispiel nach dem Wechseln zwischen Ansichten von zwei Produkten in einem Shop-System, muss nicht die gesamte Seite auf dem Server neugerendert und zum Client übertragen werden. Der Server schickt dann nur die Teile neu, die sich gegenüber der vorherigen Darstellung geändert haben. Das kann zum Beispiel eine neue Darstellung der Produktdaten im Hauptbereich sein, während für die unveränderte Sidebar kein neuer Code benötigt wird.
Unabhängig davon, ob nur ein Teil einer Website ausgetauscht wird oder die ganze Website, kann es auf Serverseite zu unterschiedlich langen Wartezeiten kommen, bis eine Komponente vollständig gerendert werden kann. Möglicherweise ist das Laden der Daten für ein Produkt schneller als das Laden der dazugehörigen Kommentare. In diesem Fall kann React angewiesen werden, auf welche Komponenten beim Rendern gewartet werden soll und welche zunächst nur einen Platzhalter zeigen. Das ist in React mithilfe von Streaming und der Suspense API möglich. Je nach fachlicher Anforderung kann damit deklarativ beschrieben werden, ob zum Beispiel auf das Rendern von Produkt- und Kommentaransicht gewartet werden soll, oder ob eine der beiden Komponenten schon zum Client geschickt werden soll, auch wenn die andere noch fertig gerendert wird.
Mit vielen Fullstack-Frameworks können interaktive Komponenten so gebaut werden, dass sie im Browser auch ohne JavaScript funktionieren, aber dennoch eine Grundfunktionalität zur Verfügung stellen. Etwa wenn das JavaScript wegen schlechter Internetverbindung nicht oder nur langsam lädt. Sobald das JavaScript dann bereit ist, kann die Anwendung dem Benutzer verbesserte Features zur Verfügung stellen, die ohne JavaScript nicht oder nur eingeschränkt möglich sind (zum Beispiel client-seitige Validierungen). Dieses Verhalten wird auch als Progressive Enhancement bezeichnet, weil die Anwendung grundsätzlich funktioniert, aber nach-und-nach durch das Laden von JavaScript erweitert wird.
Die technische Umsetzung dieser Lösungen unterscheidet sich in den Frameworks. Allerdings bietet React neuerdings die React Server Components (RSC) an. Das sind Komponenten, deren JavaScript nicht im Client vorhanden sein soll, weil sie ausschließlich statischen Content erzeugen. Obwohl der Name es suggeriert, werden Server Components nicht ausschließlich auf dem Server gerendert, sondern können auch schon zur Build-Zeit gerendert werden. Damit stehen sie dann zur Laufzeit bereits vollständig zur Verfügung und müssen nicht bei jedem Request erneut erzeugt werden. Das funktioniert aber nur, wenn sie keine Request-spezifischen Informationen benötigen. Da diese Komponenten auf dem Server (zur Lauf- oder Build-Zeit) gerendert werden, haben sie Zugriff auf die Serverinfrastruktur und können deshalb beispielsweise auf das Filesystem oder Datenbanken direkt zugreifen. Damit können sie zumindest Teile klassischer Backend-Aufgaben übernehmen, weswegen solche Anwendungen dann auch als Fullstack-Anwendungen bezeichnet werden.
Um React Server Components in der eigenen Anwendung zu verwenden ist ein komplexes Tooling notwendig. Es wird ein RSC-kompatibler Server (zur Laufzeit) und/oder ein RSC-kompatibles Build Tool benötigt. Auch aus diesem Grund hat das React-Team die eingangs erwähnte Fullstack-Empfehlung ausgesprochen, denn das Implementieren solcher Tools ist nicht trivial.
Natürlich stehen in React auch weiterhin die „klassischen“ Komponenten zur Verfügung, die in Abgrenzung nun als Client Components (CC) bezeichnet werden. Auch hier ist der Name leider irreführend, denn Client Components können auch auf dem Server (vor-)gerendert werden. Im Gegensatz zu den Server Components wird ihr JavaScript-Code aber auch auf den Browser übertragen und dort bei Bedarf ausgeführt. Diese Komponenten enthalten die interaktiven Teile der Anwendung und entsprechen den klassischen Komponenten, die aus React bekannt sind. Da die APIs der beiden Komponenten-Arten weitgehend identisch sind, ist es für das Build Tool nicht immer erkennbar, ob sie (und damit auch der Code, den sie verwenden) auf dem Server- und/oder auf dem Client ausgeführt werden sollen. Daher müssen einzelne Code-Stellen mit einer React-Direktive („use client“ bzw. „use server“) gekennzeichnet werden.

Über den Autor: Nils Hartmann
Nils Hartmann ist freiberuflicher Softwareentwickler und -architekt aus Hamburg. Er beschäftigt sich seit mehr als 20 Jahren mit der Entwicklung von Software. Sein Schwerpunkt liegt auf Java-basierten Backend-Services mit Spring Boot sowie der Entwicklung von Frontends mit React und TypeScript. In seinen Projekten setzt er gerne GraphQL ein. Er ist Co-Autor des Buchs „React – die praktische Einführung“ (dpunkt.Verlag).