Es ist mal wieder so weit: Eine neue Release-Version von Next.js ist da — Next.js 15. Mit diesem Update setzt sich Vercel das Ziel, neue Massstäbe im Bereich von Performance, Sicherheit und Entwicklerfreundlichkeit zu setzen. Die verbesserte Performance soll dabei nicht nur Entwickler, sondern auch die Endnutzer erfreuen. In diesem Techup schauen wir uns die neuen Features an, die mit dem Upgrade auf Version 15 kommen. Dies versuchen wir natürlich anhand einiger Beispiele, die ihr euch auch in unserem Techup-Repository ansehen könnt.
Announcments
Automatische Updates mit Codemods
Jeder Entwickler kennt das Problem: Sobald eine neue Version veröffentlicht wird, steht man des Öfteren vor der Herausforderung, dass sich die API geändert hat. Genau hier setzen Codemods an. Dabei handelt es sich um eine automatische Upgrade-Möglichkeit. Dies ist vor allem dann hilfreich, wenn es grössere Änderungen gibt, denn Next.js führt diese Updates automatisch durch.
Weitere Versionen, die geupdatet werden, sind Next.js, React, React Hooks und ESLint.
Dies kann wie folgt gemacht werden direkt über die CLI:
|
|
Die Vorteile sind natürlich klar, wenn das Skript auch wirklich funktioniert: Es ist effizient, da Änderungen automatisch in Sekundenschnelle vorgenommen werden. Ausserdem ist es fehlerfrei, da menschliche Fehler vermieden werden. Die Einfachheit des Prozesses liegt darin, dass keine manuellen Anpassungen erforderlich sind. Dadurch wird eine erhebliche Zeitersparnis erreicht, da weniger Aufwand für die Entwickler entsteht. Zusätzlich wird Konsistenz gewährleistet, da alle Projekte die gleiche Version nutzen können.
Asynchrone Request-APIs (Breaking Change)
Beim ersten Breaking Change handelt es sich um verschiedene Request-APIs. Diese werden in der neuen Version zu asynchronen APIs wie cookies
, headers
und params
umgestellt. Dies führt zu einem Paradigmenwechsel bei der serverseitigen Verarbeitung. Das Ziel ist jedoch klar: bessere Performance.
Bei der üblichen Server-Side-Rendering-Verarbeitung wird der Server solange blockiert, bis der Request vollständig empfangen wurde. Dies hat natürlich zur Folge, dass man mit Wartezeiten rechnen muss. In diesem Fall muss man mit dem Rendern von Inhalten warten, bis Request-Daten wie Header oder Cookies vollständig geladen sind.
Durch die neue asynchrone API kann man Cookies oder Header erst dann laden, wenn man sie auch wirklich braucht.
Next.js 14 (synchron)
In diesem Beispiel wird der params
-Parameter synchron verwendet, was bedeutet, dass der gesamte Request abgewartet wird, bevor der Inhalt weiterverarbeitet werden kann.
|
|
Next.js 15 (asynchron)
In diesem Beispiel wird params
als asynchrone Promise verarbeitet, sodass die Werte erst abgerufen werden, wenn sie tatsächlich benötigt werden, was die Effizienz steigert und die Blockadezeiten reduziert.
|
|
Verbesserte Caching-Mechanismen
Optimierung der Caching-Strategie für GET-Routen-Handler
Bei Next.js war es standardmässig so, dass GET-Handler gecacht wurden, ausser sie haben bewusst dynamische Funktionen oder Konfigurationen genutzt. Dies konnte natürlich bei Seiten mit dynamischem Inhalt zu Problemen führen, insbesondere wenn der Inhalt abgerufen werden musste. Mit Next.js 15 wurde dies nun so angepasst, dass standardmässig nichts mehr gecacht wird. Allerdings gibt es ein paar Sonderfälle, zu denen sitemap.ts
, opengraph-image.tsx
und icon.tsx
gehören. Diese bleiben weiterhin statisch gecacht.
Der Client-Router-Cache wurde in Next.js 14 mit einem aggressiven Caching-Verhalten eingeführt. Ein Beispiel: Auch Seiten mit dynamischen Routen (z. B. /product/[id]
) wurden für 30 Sekunden gecacht, obwohl sich die zugrunde liegenden Daten während dieser Zeit ändern konnten. Mit dem Upgrade auf Next.js 15 wird der Wert stale-time
für Seiten standardmässig auf 0 gesetzt.
Zu erwähnen ist jedoch, dass das Verhalten in folgenden Punkten gleich geblieben ist:
- Sharded Layouts: Layout-Komponenten werden weiterhin nicht erneut vom Server geladen, um Partial Rendering zu unterstützen.
- Back/Forward-Navigation: Bei der Navigation zurück oder vorwärts werden Daten aus dem Cache wiederhergestellt, um die Scrollposition zu bewahren.
- loading.js-Komponente: Diese wird weiterhin für 5 Minuten (oder entsprechend der Konfiguration) gecacht.
Allgemein lässt sich sagen, dass sich das Caching-Verhalten von einem Opt-out- zu einem Opt-in-Verfahren geändert hat. Hier ein Beispiel für die Möglichkeit, das Caching granular pro Request zu steuern:
|
|
Integration von React 19
Mit dem Upgrade von Next wird auch der Release candidate von React 19 direkt verwendet. React 19 bringt wesentliche Verbesserungen wie den neuen React Compiler, der React-Code in Plain JavaScript übersetzt und die Startgeschwindigkeit von Anwendungen verdoppelt, sowie die Actions API für einfacheres Formularhandling. Server Components und Web Components sorgen für optimierte Ladegeschwindigkeit und verbesserte Integration nativer HTML-Elemente. Zusätzliche Features wie Asset Loading, Document Metadata, erweiterte Hooks und eine neue use
API verbessern die Performance und Benutzerfreundlichkeit, während die Fehlerbehandlung und Hydration ebenfalls optimiert wurden.
Verbesserte Fehlerbehandlung bei Hydration-Problemen
Ein häufiger Grund für Hydration-Fehler ist, dass sich der Inhalt zwischen Server-Side Rendering (SSR) und der clientseitigen Hydration unterscheidet. Dies geschieht oft durch dynamische Werte, wie beispielsweise bei Date.now()
oder Math.random()
.
Next.js 15
Next.js 14
Das Beispiel zeigt, wie Next.js 15 die Fehlermeldungen für Hydration-Fehler verbessert und Entwicklern klare Anleitungen zur Behebung bietet. Während Next.js 14.1 eher vage Warnungen ausgab, hebt Next.js 15 das Debugging auf ein neues Niveau, indem es spezifische Quellcode-Referenzen und Lösungsvorschläge liefert.
Stabilität und Performance mit Turbopack
Mit der neuen Version von Next.js ist Turbopack nun stabil und bereit für den täglichen Gebrauch. Es lässt sich über die package.json
aktivieren, indem --turbo
an das dev
-Script angehängt wird.
|
|
Die Vorteile ergeben sich vor allem durch die gesteigerte Performance. Vercel berichtet, dass sie mit ihrer eigenen Next.js-App einen bis zu 76,7 % schnelleren Server-Start in der Entwicklungsumgebung erreichen konnten. Auch während der Entwicklung erzielten sie dank des optimierten Fast Refreshs bis zu 96,3 % schnellere Code-Aktualisierungen, da z. B. nicht notwendige Module nicht erneut kompiliert werden müssen.
Auch die Stabilität und Konsistenz der Kompilierzeiten sind besser geworden. Ausserdem gilt Turbopack als zukunftssicher, da einige weitere Features geplant sind. Dazu gehört unter anderem ein persistenter Cache, um bereits kompilierten Code über Neustarts hinweg nutzen zu können.
Beispiel Next.js 15 mit Turbopack:
|
|
Und dieselbe Applikation ohne Turbopack:
|
|
Statische vs. Dynamische Routen: Der Static Route Indicator
Diese neue Funktion unterstützt den Entwicklungsprozess, indem sie anzeigt, ob es sich um eine statische oder eine dynamische Route handelt. Durch die Markierung unten links kann direkt erkannt werden, wie die Seite gerendert wird.
[!NOTE]
Statische Routen: Diese werden zur Build-Zeit einmalig generiert und direkt aus dem Cache ausgeliefert (SSG).
Dynamische Routen: Dies sind Seiten, die bei jeder Anfrage neu auf dem Server gerendert werden (SSR) oder dynamische Anpassungen enthalten.
Führt man ein next build
aus, werden alle Routen aufgelistet, die in der Applikation verfügbar sind. Dabei werden statische Routen mit ○
und dynamische Routen mit ƒ
dargestellt.
|
|
Ruft man nun die Startseite auf, sieht man unten links den Static Route Indicator:
Ruft man dagegen /techup/1
auf, ist dieser Indicator nicht mehr zu sehen. Dadurch weiss man, dass diese Seite dynamisch gerendert wird.
Hinweis: Sollte keine korrekte Verwendung eines Promises erfolgen, wird der Indicator möglicherweise trotzdem angezeigt, auch wenn beispielsweise eine API-Schnittstelle aufgerufen wird.
Ausführung von Code nach der Antwort mit unstable_after (Experimentell)
Dieses Feature zielt ebenfalls darauf ab, die Performance zu erhöhen. Oft gibt es Aufgaben, die den Benutzer nicht unmittelbar betreffen und bei denen es daher unnötig ist, dass er darauf wartet. Solche Aufgaben umfassen beispielsweise das Logging von Ereignissen oder das Erfassen von Analysedaten. Bisher war dies nicht möglich, da Serverless-Funktionen ihre Rechenzeit beenden, sobald die Antwort abgeschlossen ist. Dadurch war es nicht möglich, nachgelagerte Aufgaben durchzuführen. Mit diesem Feature ist es nun möglich, die Hauptaufgabe der Komponente durchzuführen, wie z. B. das Rendern der Seite. In einem zweiten Schritt kann dann die sekundäre Aufgabe abgearbeitet werden.
Dies ist eines der experimentellen Features, die zunächst über die Einstellungen aktiviert werden müssen. Dies kann in der Datei next.config.ts
wie folgt erfolgen:
|
|
Erstellen wir nun eine einfache Seite, die das neue unstable_after
-Feature verwendet:
|
|
Ruft man nun die erstellte Seite auf, bekommt man den Zeitstempel des Aufrufs:
In diesem Beispiel sieht man den Unterschied zwischen der Hauptaufgabe und der nachgelagerten Aufgabe nur anhand der Zeitstempel in der Konsole, die auf Tausendstelsekunden genau sind:
|
|
Einführung der instrumentation.js
zur Fehlerüberwachung
Nun werden wir uns die Datei instrumentation.js
angucken, diese soll zur besseren Nachverfolgbarkeit und Fehlerüberwachung in einer Next.js-App helfen. Dabei kann bspw. eine Funktionen erstellt werden, die es ermöglichen, Fehler zu erfassen und sie an einen Observability-Service weiterzuleiten.
Die Datei instrumentation.js
wird im root-Verzeichnis des Projekts angelegt, wie in der folgenden Verzeichnisstruktur dargestellt:
|
|
Zunächst muss die Funktion register
exportiert werden. Diese wird automatisch beim Start des Next.js-Servers aufgerufen. Um dies zu überprüfen, fügen wir ein Log-Statement ein.
Die neue Funktion onRequestError
wird nun automatisch aufgerufen, sobald ein Fehler auftritt. In unserem Beispiel wird hauptsächlich ein Fehlerprotokoll (console.error
) geschrieben. In einer produktiven Umgebung würde an dieser Stelle ein fetch
-Aufruf an einen Observability-Service erfolgen, um die Fehlerinformationen weiterzuleiten.
|
|
Beim Starten des Servers sieht man die Log-Nachricht “Observability SDK initialized
” aus der register
-Funktion, die bestätigt, dass die Initialisierung erfolgreich war:
|
|
Wenn der Endpunkt /api/instrumentation
aufgerufen wird, sieht man das Log, das innerhalb der Methode onRequestError
geworfen wird. Dieses Log enthält detaillierte Informationen über den Fehler, die HTTP-Anfrage und den Kontext.
|
|
Erweiterte <Form>
-Komponente für verbesserte Formulare
Mit dieser neuen Komponente wird das standardmässige HTML-Element <form>
um zusätzliche Funktionen erweitert. Dies erleichtert die Erstellung von Formularen, da Funktionen wie Prefetching, clientseitige Navigation und weitere Verbesserungen integriert sind. Ein grosser Vorteil ist, dass das Formular auch dann funktioniert, wenn JavaScript im Browser deaktiviert ist. Da Prefetching und Navigation von Haus aus unterstützt werden, spart man als Entwickler eine Menge Code.
Hier ein einfaches Beispiel, wie die Basis der <Form>
-Komponente aussehen könnte:
|
|
Das Styling des Formulars kann natürlich angepasst werden, wie man im unteren Beispiel sieht. Wenn man auf dieser Seite beispielsweise den Suchbegriff “ipsum” eingibt und das Formular absendet:
wird man direkt auf die Seite weitergeleitet, die im action
-Parameter definiert wurde. In diesem Fall lautet die Weiterleitungsadresse: http://localhost:3002/search?query=ipsum
.
Unterstützung für next.config.ts
mit TypeScript
Mit Next.js 15 wird nun TypeScript für die Konfigurationsdatei unterstützt. Funktionell ändert sich dadurch zwar nicht viel, aber es entspricht dem modernen Standard und erleichtert die Arbeit für Entwickler, die bereits auf TypeScript setzen. Die Nutzung von TypeScript in der Konfigurationsdatei bringt einige Vorteile mit sich, wie zum Beispiel eine verbesserte Typunterstützung und Autovervollständigung durch die Entwicklungsumgebung.
Next.js 15
Hier kann die Konfigurationsdatei nun in TypeScript geschrieben werden, was eine typensichere und besser lesbare Konfiguration ermöglicht:
|
|
Next.js 14
Hier war es hingegen erforderlich, die Konfiguration mit JavaScript in einer .mjs
-Datei vorzunehmen:
|
|
Verbesserte Sicherheit für Server Actions
Mit Next.js 15 gibt es wesentliche Verbesserungen bei der Sicherheit von Server Actions. Diese Verbesserungen sollen sicherstellen, dass serverseitige Funktionen geschützt sind und nur dann ausgeführt werden, wenn es tatsächlich erforderlich ist. Zwei der neuen Funktionen in diesem Bereich sind die Verwendung von sicheren Action-IDs und die Eliminierung von nicht verwendetem Code (Dead Code Elimination).
Sichere Action-IDs für geschützte Serveraufrufe
In Next.js 15 werden Server Actions durch sichere Action-IDs referenziert, um die Sicherheit der Kommunikation zwischen dem Client und dem Server zu verbessern. Hier ist ein Beispiel.
In diesem Beispiel wird das Formular über die Action-ID testActionId
versendet, die eine Server Action referenziert. Dies stellt sicher, dass nur vertrauenswürdige Server-Aktionen aufgerufen werden können, wodurch die Sicherheit verbessert wird.
|
|
Hier ist die zugehörige Server Action:
|
|
Der Log zeigt, dass die Server Action korrekt aufgerufen wird:
|
|
Action Id im Payload:
Automatische Entfernung ungenutzter Server Actions (Dead Code Elimination)
Nicht verwendete Server Actions oder Hilfsfunktionen können ein Sicherheitsrisiko darstellen, da sie als öffentliche HTTP-Endpunkte zugänglich bleiben, selbst wenn sie nicht aktiv genutzt werden. Die Dead Code Elimination sorgt dafür, dass nur tatsächlich genutzter Code in der Produktionsversion nach dem Build enthalten ist.
Problem
- Server Actions oder Hilfsfunktionen, die nicht genutzt werden, bleiben öffentlich zugängliche HTTP-Endpunkte.
- Es besteht das Risiko einer versehentlichen Offenlegung von Funktionen, die nicht für externe Aufrufe gedacht sind.
Lösung
- Dead Code Elimination: Unbenutzte Server Actions werden während des next build automatisch entfernt.
- Nicht genutzte Funktionen werden nicht in das JavaScript-Bundle aufgenommen.
- Vorteile:
- Erhöhte Sicherheit: Keine unnötigen öffentlichen Endpunkte.
- Kleinere Bundles: Reduzierte JavaScript-Bundle-Grösse.
- Bessere Performance: Weniger Daten zum Laden und Verarbeiten.
Ein Beispiel dafür:
|
|
Optimierung des Bündelns externer Pakete
Next.js 15 führt neue Konfigurationsoptionen zur Optimierung des Bündelns externer Pakete ein. Im App Router werden externe Pakete standardmässig gebündelt, während im Pages Router mit der Option transpilePackages
spezifische Pakete zum Bündeln angegeben werden können. Diese Optimierung kann die Startleistung der Anwendung verbessern, indem die Anzahl der erforderlichen Netzwerk-Anfragen zum Laden von Abhängigkeiten reduziert wird. Mit der neuen Option bundlePagesRouterDependencies
wird das automatische Bündeln im Pages Router vereinheitlicht, und serverExternalPackages
ermöglicht das gezielte Ausschliessen bestimmter Pakete aus dem Bündelungsprozess. Diese und weitere Neuerungen in Next.js 15 sorgen dafür, dass Entwicklungsprozesse effizienter werden und die Anwendungsleistung weiter gesteigert wird.
Unterstützung für ESLint 9
Next.js 15 unterstützt nun ESLint 9, bleibt aber rückwärtskompatibel mit ESLint 8, sodass eine schrittweise Migration möglich ist, während veraltete Konfigurationsoptionen nach und nach entfernt werden.
Verbesserungen beim Entwicklungs- und Build-Prozess
Next.js 15 bringt Verbesserungen für die Entwicklung und den Build-Prozess, einschliesslich Hot Module Replacement (HMR) für Server-Komponenten, was wiederholte API-Aufrufe während der Entwicklung vermeidet, und einer schnelleren statischen Generierung, die die Build-Zeiten durch die Wiederverwendung von Render-Ergebnissen erheblich verkürzt.
Fazit
Der Fokus bei diesem Next.Js Update liegt vor allem auf Performance. Jedoch auch Sicherheit und Entwicklerfreundlichkeit. Funktionen wie after(), um nachgelagert verschiedene Aufgaben noch auszuführen, oder die Verwendung von Turbopack verbessern die Performance in unterschiedlcihen Bereichen. Zeit kann auch durch die Verbesserte Anzeige von Fehlermeldungen bei Hydration Errors oder durch den Static Indicator gespart werden.
Zwar darf man im ersten Schritt die Komplexität für die Änderungen der ansychronen API, wie headers oder params nicht unterschätzen, aber im Endeffekt bringt dies wieder einiges an Performance.
Sicherheit wird durch das Enfernen von ungenutzen Endpunkten oder die Angabe der SecurceID erhöht, was Next.js um einiges robuster macht. Spannend wird es aufjeden Fall, wenn man grössere Projekte anfängt auf die neue Version zu upgraden, aber Next.js geht aber hier auf jeden Fall in die richtige Richtung um eine mordernes Setup mit Performance und modernen Tools zu bieten.