Migration vom Pages Router zum App Router

Diese Anleitung hilft Ihnen dabei:

Aktualisierung

Node.js-Version

Die mindestens erforderliche Node.js-Version ist nun v18.17. Weitere Informationen finden Sie in der Node.js-Dokumentation.

Next.js-Version

Um auf Next.js Version 13 zu aktualisieren, führen Sie den folgenden Befehl mit Ihrem bevorzugten Paketmanager aus:

Terminal
npm install next@latest react@latest react-dom@latest

ESLint-Version

Falls Sie ESLint verwenden, müssen Sie Ihre ESLint-Version aktualisieren:

Terminal
npm install -D eslint-config-next@latest

Gut zu wissen: Möglicherweise müssen Sie den ESLint-Server in VS Code neu starten, damit die ESLint-Änderungen wirksam werden. Öffnen Sie die Befehlspalette (cmd+shift+p auf Mac; ctrl+shift+p auf Windows) und suchen Sie nach ESLint: Restart ESLint Server.

Nächste Schritte

Nach der Aktualisierung finden Sie in den folgenden Abschnitten die nächsten Schritte:

Neue Funktionen aktualisieren

Next.js 13 führte den neuen App-Router mit neuen Funktionen und Konventionen ein. Der neue Router ist im app-Verzeichnis verfügbar und koexistiert mit dem pages-Verzeichnis.

Die Aktualisierung auf Next.js 13 erfordert nicht die Verwendung des App-Routers. Sie können weiterhin pages mit neuen Funktionen verwenden, die in beiden Verzeichnissen funktionieren, wie die aktualisierte Image-Komponente, Link-Komponente, Script-Komponente und Schriftoptimierung.

<Image/>-Komponente

Next.js 12 führte Verbesserungen an der Image-Komponente mit einem temporären Import ein: next/future/image. Diese Verbesserungen umfassten weniger clientseitiges JavaScript, einfachere Möglichkeiten zur Erweiterung und Gestaltung von Bildern, bessere Barrierefreiheit und natives Browser-Lazy-Loading.

In Version 13 ist dieses neue Verhalten nun standardmäßig in next/image enthalten.

Es gibt zwei Codemods, die Ihnen bei der Migration zur neuen Image-Komponente helfen:

  • next-image-to-legacy-image-Codemod: Benennt next/image-Importe sicher und automatisch in next/legacy/image um. Bestehende Komponenten behalten das gleiche Verhalten bei.
  • next-image-experimental-Codemod: Fügt gefährlich Inline-Styles hinzu und entfernt ungenutzte Props. Dies ändert das Verhalten bestehender Komponenten, um den neuen Standard zu entsprechen. Um diesen Codemod zu verwenden, müssen Sie zuerst den next-image-to-legacy-image-Codemod ausführen.

Die <Link>-Komponente erfordert nicht mehr das manuelle Hinzufügen eines <a>-Tags als Kind. Dieses Verhalten wurde als experimentelle Option in Version 12.2 hinzugefügt und ist nun der Standard. In Next.js 13 rendert <Link> immer <a> und ermöglicht es Ihnen, Props an das zugrunde liegende Tag weiterzugeben.

Beispiel:

import Link from 'next/link'

// Next.js 12: `<a>` muss verschachtelt werden, sonst wird es ausgeschlossen
<Link href="/about">
  <a>About</a>
</Link>

// Next.js 13: `<Link>` rendert immer `<a>` im Hintergrund
<Link href="/about">
  About
</Link>

Um Ihre Links auf Next.js 13 zu aktualisieren, können Sie den new-link-Codemod verwenden.

<Script>-Komponente

Das Verhalten von next/script wurde aktualisiert, um sowohl pages als auch app zu unterstützen, aber einige Änderungen sind erforderlich, um eine reibungslose Migration zu gewährleisten:

  • Verschieben Sie alle beforeInteractive-Scripts, die Sie zuvor in _document.js eingebunden haben, in die Root-Layout-Datei (app/layout.tsx).
  • Die experimentelle worker-Strategie funktioniert noch nicht in app, und Scripts mit dieser Strategie müssen entweder entfernt oder geändert werden, um eine andere Strategie zu verwenden (z.B. lazyOnload).
  • onLoad, onReady und onError-Handler funktionieren nicht in Server-Komponenten, daher sollten Sie sie in eine Client-Komponente verschieben oder ganz entfernen.

Schriftoptimierung

Bisher half Next.js Ihnen, Schriften zu optimieren, indem Schrift-CSS inline eingebunden wurde. Version 13 führt das neue next/font-Modul ein, das Ihnen die Möglichkeit gibt, Ihre Schriftladeerfahrung anzupassen und gleichzeitig eine hervorragende Leistung und Privatsphäre zu gewährleisten. next/font wird sowohl im pages- als auch im app-Verzeichnis unterstützt.

Während Inline-CSS in pages weiterhin funktioniert, funktioniert es nicht in app. Sie sollten stattdessen next/font verwenden.

Weitere Informationen zur Verwendung von next/font finden Sie auf der Seite Schriftoptimierung.

Migration von pages zu app

🎥 Ansehen: Erfahren Sie, wie Sie den App-Router schrittweise einführen können → YouTube (16 Minuten).

Der Wechsel zum App-Router könnte das erste Mal sein, dass Sie React-Funktionen verwenden, auf denen Next.js aufbaut, wie Server-Komponenten, Suspense und mehr. In Kombination mit neuen Next.js-Funktionen wie Spezialdateien und Layouts bedeutet die Migration neue Konzepte, mentale Modelle und Verhaltensänderungen, die es zu lernen gilt.

Wir empfehlen, die kombinierte Komplexität dieser Aktualisierungen zu reduzieren, indem Sie Ihre Migration in kleinere Schritte unterteilen. Das app-Verzeichnis ist absichtlich so gestaltet, dass es gleichzeitig mit dem pages-Verzeichnis funktioniert, um eine schrittweise Migration Seite für Seite zu ermöglichen.

  • Das app-Verzeichnis unterstützt verschachtelte Routen und Layouts. Mehr erfahren.
  • Verwenden Sie verschachtelte Ordner, um Routen zu definieren, und eine spezielle page.js-Datei, um ein Routensegment öffentlich zugänglich zu machen. Mehr erfahren.
  • Spezialdateikonventionen werden verwendet, um die Benutzeroberfläche für jedes Routensegment zu erstellen. Die häufigsten Spezialdateien sind page.js und layout.js.
    • Verwenden Sie page.js, um eine benutzerdefinierte Benutzeroberfläche für eine Route zu definieren.
    • Verwenden Sie layout.js, um eine Benutzeroberfläche zu definieren, die über mehrere Routen hinweg geteilt wird.
    • Die Dateierweiterungen .js, .jsx oder .tsx können für Spezialdateien verwendet werden.
  • Sie können andere Dateien wie Komponenten, Styles, Tests und mehr im app-Verzeichnis zusammenstellen. Mehr erfahren.
  • Datenabruffunktionen wie getServerSideProps und getStaticProps wurden durch eine neue API in app ersetzt. getStaticPaths wurde durch generateStaticParams ersetzt.
  • pages/_app.js und pages/_document.js wurden durch ein einzelnes app/layout.js-Root-Layout ersetzt. Mehr erfahren.
  • pages/_error.js wurde durch granulare error.js-Spezialdateien ersetzt. Mehr erfahren.
  • pages/404.js wurde durch die not-found.js-Datei ersetzt.
  • pages/api/*-API-Routen wurden durch die route.js-Spezialdatei (Route-Handler) ersetzt.

Schritt 1: Erstellen des app-Verzeichnisses

Aktualisieren Sie auf die neueste Next.js-Version (erfordert 13.4 oder höher):

npm install next@latest

Erstellen Sie dann ein neues app-Verzeichnis im Stammverzeichnis Ihres Projekts (oder im src/-Verzeichnis).

Schritt 2: Erstellen eines Root-Layouts

Erstellen Sie eine neue app/layout.tsx-Datei im app-Verzeichnis. Dies ist ein Root-Layout, das für alle Routen in app gilt.

export default function RootLayout({
  // Layouts müssen eine children-Prop akzeptieren.
  // Diese wird mit verschachtelten Layouts oder Seiten gefüllt.
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • Das app-Verzeichnis muss ein Root-Layout enthalten.
  • Das Root-Layout muss <html>- und <body>-Tags definieren, da Next.js diese nicht automatisch erstellt.
  • Das Root-Layout ersetzt die pages/_app.tsx- und pages/_document.tsx-Dateien.
  • Die Dateierweiterungen .js, .jsx oder .tsx können für Layout-Dateien verwendet werden.

Um <head>-HTML-Elemente zu verwalten, können Sie die integrierte SEO-Unterstützung verwenden:

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}

Migration von _document.js und _app.js

Falls Sie eine bestehende _app- oder _document-Datei haben, können Sie deren Inhalte (z.B. globale Styles) in das Root-Layout (app/layout.tsx) kopieren. Styles in app/layout.tsx gelten nicht für pages/*. Sie sollten _app/_document während der Migration behalten, um zu verhindern, dass Ihre pages/*-Routen brechen. Sobald die Migration abgeschlossen ist, können Sie sie sicher löschen.

Falls Sie React Context-Provider verwenden, müssen diese in eine Client-Komponente verschoben werden.

Migration des getLayout()-Musters zu Layouts (Optional)

Next.js empfahl, eine Eigenschaft zu Page-Komponenten hinzuzufügen, um pro-Seite-Layouts im pages-Verzeichnis zu erreichen. Dieses Muster kann durch die native Unterstützung für verschachtelte Layouts im app-Verzeichnis ersetzt werden.

Vorher- und Nachher-Beispiel anzeigen

Vorher

components/DashboardLayout.js
export default function DashboardLayout({ children }) {
  return (
    <div>
      <h2>My Dashboard</h2>
      {children}
    </div>
  )
}
pages/dashboard/index.js
import DashboardLayout from '../components/DashboardLayout'

export default function Page() {
  return <p>My Page</p>
}

Page.getLayout = function getLayout(page) {
  return <DashboardLayout>{page}</DashboardLayout>
}

Nachher

  • Entfernen Sie die Page.getLayout-Eigenschaft aus pages/dashboard/index.js und folgen Sie den Schritten zur Migration von Seiten in das app-Verzeichnis.

    app/dashboard/page.js
    export default function Page() {
      return <p>My Page</p>
    }
  • Verschieben Sie den Inhalt von DashboardLayout in eine neue Client-Komponente, um das Verhalten des pages-Verzeichnisses beizubehalten.

    app/dashboard/DashboardLayout.js
    'use client' // Diese Direktive sollte am Anfang der Datei stehen, vor allen Imports.
    
    // Dies ist eine Client-Komponente
    export default function DashboardLayout({ children }) {
      return (
        <div>
          <h2>My Dashboard</h2>
          {children}
        </div>
      )
    }
  • Importieren Sie DashboardLayout in eine neue layout.js-Datei im app-Verzeichnis.

    app/dashboard/layout.js
    import DashboardLayout from './DashboardLayout'
    
    // Dies ist eine Server-Komponente
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>
    }
  • Sie können nicht-interaktive Teile von DashboardLayout.js (Client-Komponente) schrittweise in layout.js (Server-Komponente) verschieben, um die Menge an Komponenten-JavaScript zu reduzieren, die Sie an den Client senden.

Schritt 3: Migration von next/head

Im pages-Verzeichnis wird die next/head-React-Komponente verwendet, um <head>-HTML-Elemente wie title und meta zu verwalten. Im app-Verzeichnis wird next/head durch die neue integrierte SEO-Unterstützung ersetzt.

Vorher:

import Head from 'next/head'

export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}

Nachher:

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My Page Title',
}

export default function Page() {
  return '...'
}

Alle Metadatenoptionen anzeigen.

Schritt 4: Migration von Seiten

  • Seiten im app-Verzeichnis sind standardmäßig Server Components (Server-Komponenten). Dies unterscheidet sich vom pages-Verzeichnis, wo Seiten Client Components (Client-Komponenten) sind.
  • Die Datenabfrage (Data Fetching) hat sich in app geändert. getServerSideProps, getStaticProps und getInitialProps wurden durch eine einfachere API ersetzt.
  • Das app-Verzeichnis verwendet verschachtelte Ordner, um Routen zu definieren, und eine spezielle page.js-Datei, um ein Routensegment öffentlich zugänglich zu machen.
  • pages-Verzeichnisapp-VerzeichnisRoute
    index.jspage.js/
    about.jsabout/page.js/about
    blog/[slug].jsblog/[slug]/page.js/blog/post-1

Wir empfehlen, die Migration einer Seite in zwei Hauptschritte aufzuteilen:

  • Schritt 1: Verschieben der standardmäßig exportierten Page-Komponente in eine neue Client-Komponente.
  • Schritt 2: Importieren der neuen Client-Komponente in eine neue page.js-Datei im app-Verzeichnis.

Gut zu wissen: Dies ist der einfachste Migrationspfad, da er das vergleichbarste Verhalten zum pages-Verzeichnis aufweist.

Schritt 1: Erstellen einer neuen Client-Komponente

  • Erstellen Sie eine neue separate Datei im app-Verzeichnis (z.B. app/home-page.tsx oder ähnlich), die eine Client-Komponente exportiert. Um Client-Komponenten zu definieren, fügen Sie die 'use client'-Direktive am Anfang der Datei ein (vor allen Imports).
    • Ähnlich wie beim Pages Router gibt es einen Optimierungsschritt, um Client-Komponenten beim ersten Seitenaufruf als statisches HTML vorzurrendern.
  • Verschieben Sie die standardmäßig exportierte Page-Komponente von pages/index.js nach app/home-page.tsx.
'use client'

// Dies ist eine Client-Komponente (wie Komponenten im `pages`-Verzeichnis)
// Sie empfängt Daten als Props, hat Zugriff auf State und Effekte und wird
// beim ersten Seitenaufruf auf dem Server vorgerendert.
export default function HomePage({ recentPosts }) {
  return (
    <div>
      {recentPosts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

Schritt 2: Erstellen einer neuen Seite

  • Erstellen Sie eine neue app/page.tsx-Datei im app-Verzeichnis. Dies ist standardmäßig eine Server-Komponente.

  • Importieren Sie die home-page.tsx-Client-Komponente in die Seite.

  • Falls Sie in pages/index.js Daten abgefragt haben, verschieben Sie die Datenabfragelogik direkt in die Server-Komponente unter Verwendung der neuen Datenabfrage-APIs. Weitere Details finden Sie im Leitfaden zur Datenabfrage-Migration.

    // Importieren Sie Ihre Client-Komponente
    import HomePage from './home-page'
    
    async function getPosts() {
      const res = await fetch('https://...')
      const posts = await res.json()
      return posts
    }
    
    export default async function Page() {
      // Daten direkt in einer Server-Komponente abfragen
      const recentPosts = await getPosts()
      // Abgefragte Daten an Ihre Client-Komponente weiterleiten
      return <HomePage recentPosts={recentPosts} />
    }
  • Falls Ihre vorherige Seite useRouter verwendet hat, müssen Sie auf die neuen Routing-Hooks umstellen. Mehr erfahren.

  • Starten Sie Ihren Entwicklungsserver und rufen Sie http://localhost:3000 auf. Sie sollten Ihre bestehende Index-Route sehen, die nun über das app-Verzeichnis bereitgestellt wird.

Schritt 5: Migration von Routing-Hooks

Ein neuer Router wurde hinzugefügt, um das neue Verhalten im app-Verzeichnis zu unterstützen.

In app sollten Sie die drei neuen Hooks verwenden, die aus next/navigation importiert werden: useRouter(), usePathname() und useSearchParams().

  • Der neue useRouter-Hook wird aus next/navigation importiert und verhält sich anders als der useRouter-Hook in pages, der aus next/router importiert wird.
  • Der neue useRouter gibt nicht den pathname-String zurück. Verwenden Sie stattdessen den separaten usePathname-Hook.
  • Der neue useRouter gibt nicht das query-Objekt zurück. Suchparameter und dynamische Routenparameter sind jetzt getrennt. Verwenden Sie stattdessen die Hooks useSearchParams und useParams.
  • Sie können useSearchParams und usePathname kombinieren, um auf Seitenänderungen zu reagieren. Weitere Details finden Sie im Abschnitt Router-Ereignisse (Router Events).
  • Diese neuen Hooks werden nur in Client-Komponenten unterstützt. Sie können nicht in Server-Komponenten verwendet werden.
'use client'

import { useRouter, usePathname, useSearchParams } from 'next/navigation'

export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()

  // ...
}

Zusätzlich weist der neue useRouter-Hook folgende Änderungen auf:

  • isFallback wurde entfernt, da fallback ersetzt wurde.
  • Die Werte locale, locales, defaultLocales, domainLocales wurden entfernt, da die integrierten i18n-Funktionen von Next.js im app-Verzeichnis nicht mehr notwendig sind. Mehr über i18n erfahren.
  • basePath wurde entfernt. Die Alternative wird nicht Teil von useRouter sein. Sie wurde noch nicht implementiert.
  • asPath wurde entfernt, da das Konzept von as aus dem neuen Router entfernt wurde.
  • isReady wurde entfernt, da es nicht mehr notwendig ist. Während des statischen Renderings (Static Rendering) wird jede Komponente, die den useSearchParams()-Hook verwendet, den Prerendering-Schritt überspringen und stattdessen zur Laufzeit auf dem Client gerendert.
  • route wurde entfernt. usePathname oder useSelectedLayoutSegments() bieten eine Alternative.

Siehe die useRouter()-API-Referenz.

Gemeinsame Nutzung von Komponenten zwischen pages und app

Um Komponenten zwischen dem pages- und app-Router kompatibel zu halten, verweisen Sie auf den useRouter-Hook aus next/compat/router. Dies ist der useRouter-Hook aus dem pages-Verzeichnis, der jedoch für die gemeinsame Nutzung von Komponenten zwischen den Routern gedacht ist. Sobald Sie bereit sind, ihn nur im app-Router zu verwenden, aktualisieren Sie auf den neuen useRouter aus next/navigation.

Schritt 6: Migration von Datenabfrage-Methoden

Das pages-Verzeichnis verwendet getServerSideProps und getStaticProps, um Daten für Seiten abzufragen. Im app-Verzeichnis wurden diese bisherigen Datenabfragefunktionen durch eine einfachere API ersetzt, die auf fetch() und asynchronen React Server Components basiert.

export default async function Page() {
  // Diese Anfrage sollte zwischengespeichert werden, bis sie manuell ungültig gemacht wird.
  // Ähnlich wie `getStaticProps`.
  // `force-cache` ist der Standardwert und kann weggelassen werden.
  const staticData = await fetch(`https://...`, { cache: 'force-cache' })

  // Diese Anfrage sollte bei jeder Anfrage neu abgefragt werden.
  // Ähnlich wie `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })

  // Diese Anfrage sollte mit einer Lebensdauer von 10 Sekunden zwischengespeichert werden.
  // Ähnlich wie `getStaticProps` mit der `revalidate`-Option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })

  return <div>...</div>
}

Serverseitiges Rendering (getServerSideProps)

Im pages-Verzeichnis wird getServerSideProps verwendet, um Daten auf dem Server abzufragen und Props an die standardmäßig exportierte React-Komponente in der Datei weiterzuleiten. Das initiale HTML für die Seite wird vom Server vorgerendert, gefolgt vom "Hydrieren" der Seite im Browser (um sie interaktiv zu machen).

pages/dashboard.js
// `pages`-Verzeichnis

export async function getServerSideProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()

  return { props: { projects } }
}

export default function Dashboard({ projects }) {
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

Im App Router können wir unsere Datenabfrage direkt in unseren React-Komponenten mit Server Components (Server-Komponenten) platzieren. Dies ermöglicht es uns, weniger JavaScript an den Client zu senden, während das gerenderte HTML vom Server beibehalten wird.

Durch Setzen der cache-Option auf no-store können wir angeben, dass die abgefragten Daten niemals zwischengespeichert werden sollen. Dies ist ähnlich wie getServerSideProps im pages-Verzeichnis.

// `app`-Verzeichnis

// Diese Funktion kann beliebig benannt werden
async function getProjects() {
  const res = await fetch(`https://...`, { cache: 'no-store' })
  const projects = await res.json()

  return projects
}

export default async function Dashboard() {
  const projects = await getProjects()

  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

Zugriff auf das Request-Objekt

Im pages-Verzeichnis können Sie anforderungsbasierte Daten basierend auf der Node.js HTTP-API abrufen.

Beispielsweise können Sie das req-Objekt aus getServerSideProps abrufen und damit die Cookies und Header der Anfrage auslesen.

pages/index.js
// `pages`-Verzeichnis

export async function getServerSideProps({ req, query }) {
  const authHeader = req.getHeaders()['authorization'];
  const theme = req.cookies['theme'];

  return { props: { ... }}
}

export default function Page(props) {
  return ...
}

Das app-Verzeichnis stellt neue schreibgeschützte Funktionen zum Abrufen von Anfragedaten bereit:

// `app`-Verzeichnis
import { cookies, headers } from 'next/headers'

async function getData() {
  const authHeader = (await headers()).get('authorization')

  return '...'
}

export default async function Page() {
  // Sie können `cookies` oder `headers` direkt in Server-Komponenten
  // oder in Ihrer Datenabfragefunktion verwenden
  const theme = (await cookies()).get('theme')
  const data = await getData()
  return '...'
}

Statische Seitengenerierung (getStaticProps)

Im pages-Verzeichnis wird die Funktion getStaticProps verwendet, um eine Seite zur Build-Zeit vorzurrendern. Diese Funktion kann verwendet werden, um Daten von einer externen API oder direkt aus einer Datenbank abzufragen und diese Daten während des Builds an die gesamte Seite weiterzuleiten.

pages/index.js
// `pages`-Verzeichnis

export async function getStaticProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()

  return { props: { projects } }
}

export default function Index({ projects }) {
  return projects.map((project) => <div>{project.name}</div>)
}

Im app-Verzeichnis wird die Datenabfrage mit fetch() standardmäßig auf cache: 'force-cache' gesetzt, was die Anfragedaten bis zur manuellen Ungültigmachung zwischenspeichert. Dies ist ähnlich wie getStaticProps im pages-Verzeichnis.

app/page.js
// `app`-Verzeichnis

// Diese Funktion kann beliebig benannt werden
async function getProjects() {
  const res = await fetch(`https://...`)
  const projects = await res.json()

  return projects
}

export default async function Index() {
  const projects = await getProjects()

  return projects.map((project) => <div>{project.name}</div>)
}

Dynamische Pfade (getStaticPaths)

Im pages-Verzeichnis wird die Funktion getStaticPaths verwendet, um die dynamischen Pfade zu definieren, die zur Build-Zeit vorgerendert werden sollen.

pages/posts/[id].js
// `pages`-Verzeichnis
import PostLayout from '@/components/post-layout'

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
  }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  return { props: { post } }
}

export default function Post({ post }) {
  return <PostLayout post={post} />
}

Im app-Verzeichnis wird getStaticPaths durch generateStaticParams ersetzt.

generateStaticParams verhält sich ähnlich wie getStaticPaths, bietet jedoch eine vereinfachte API für die Rückgabe von Routenparametern und kann innerhalb von Layouts verwendet werden. Die Rückgabeform von generateStaticParams ist ein Array von Segmenten anstelle eines Arrays verschachtelter param-Objekte oder eines Strings mit aufgelösten Pfaden.

app/posts/[id]/page.js
// `app`-Verzeichnis
import PostLayout from '@/components/post-layout'

export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}

async function getPost(params) {
  const res = await fetch(`https://.../posts/${(await params).id}`)
  const post = await res.json()

  return post
}

export default async function Post({ params }) {
  const post = await getPost(params)

  return <PostLayout post={post} />
}

Der Name generateStaticParams ist für das neue Modell im app-Verzeichnis passender als getStaticPaths. Das Präfix get wird durch das beschreibendere generate ersetzt, das besser allein steht, da getStaticProps und getServerSideProps nicht mehr notwendig sind. Das Suffix Paths wird durch Params ersetzt, das besser für verschachteltes Routing mit mehreren dynamischen Segmenten geeignet ist.


Ersetzen von fallback

Im pages-Verzeichnis wird die Eigenschaft fallback, die von getStaticPaths zurückgegeben wird, verwendet, um das Verhalten einer Seite zu definieren, die nicht zur Build-Zeit vorgerendert wurde. Diese Eigenschaft kann auf true gesetzt werden, um eine Fallback-Seite anzuzeigen, während die Seite generiert wird, auf false, um eine 404-Seite anzuzeigen, oder auf 'blocking', um die Seite zur Laufzeit zu generieren.

pages/posts/[id].js
// `pages`-Verzeichnis

export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking'
  };
}

export async function getStaticProps({ params }) {
  ...
}

export default function Post({ post }) {
  return ...
}

Im app-Verzeichnis steuert die Eigenschaft config.dynamicParams, wie mit Parametern umgegangen wird, die nicht in generateStaticParams enthalten sind:

  • true: (Standard) Dynamische Segmente, die nicht in generateStaticParams enthalten sind, werden bei Bedarf generiert.
  • false: Dynamische Segmente, die nicht in generateStaticParams enthalten sind, geben einen 404-Fehler zurück.

Dies ersetzt die Option fallback: true | false | 'blocking' von getStaticPaths im pages-Verzeichnis. Die Option fallback: 'blocking' ist in dynamicParams nicht enthalten, da der Unterschied zwischen 'blocking' und true mit Streaming vernachlässigbar ist.

app/posts/[id]/page.js
// `app`-Verzeichnis

export const dynamicParams = true;

export async function generateStaticParams() {
  return [...]
}

async function getPost(params) {
  ...
}

export default async function Post({ params }) {
  const post = await getPost(params);

  return ...
}

Wenn dynamicParams auf true (Standard) gesetzt ist, wird ein angefordertes Routensegment, das noch nicht generiert wurde, serverseitig gerendert und zwischengespeichert.

Incremental Static Regeneration (getStaticProps mit revalidate)

Im pages-Verzeichnis ermöglicht die Funktion getStaticProps das Hinzufügen eines revalidate-Felds, um eine Seite nach einer bestimmten Zeit automatisch neu zu generieren.

pages/index.js
// `pages`-Verzeichnis

export async function getStaticProps() {
  const res = await fetch(`https://.../posts`)
  const posts = await res.json()

  return {
    props: { posts },
    revalidate: 60,
  }
}

export default function Index({ posts }) {
  return (
    <Layout>
      <PostList posts={posts} />
    </Layout>
  )
}

Im app-Verzeichnis kann das Abrufen von Daten mit fetch() revalidate verwenden, wodurch die Anfrage für die angegebene Anzahl von Sekunden zwischengespeichert wird.

app/page.js
// `app`-Verzeichnis

async function getPosts() {
  const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
  const data = await res.json()

  return data.posts
}

export default async function PostList() {
  const posts = await getPosts()

  return posts.map((post) => <div>{post.name}</div>)
}

API-Routen

API-Routen funktionieren weiterhin im pages/api-Verzeichnis ohne Änderungen. Sie wurden jedoch im app-Verzeichnis durch Route Handler ersetzt.

Route Handler ermöglichen es Ihnen, benutzerdefinierte Anfragehandler für eine bestimmte Route mit den Web-Request- und Response-APIs zu erstellen.

export async function GET(request: Request) {}

Gut zu wissen: Wenn Sie zuvor API-Routen verwendet haben, um eine externe API vom Client aus aufzurufen, können Sie jetzt stattdessen Server Components verwenden, um Daten sicher abzurufen. Erfahren Sie mehr über Datenabruf.

Single-Page Applications

Wenn Sie gleichzeitig von einer Single-Page Application (SPA) zu Next.js migrieren, lesen Sie unsere Dokumentation, um mehr zu erfahren.

Schritt 7: Styling

Im pages-Verzeichnis sind globale Stylesheets auf pages/_app.js beschränkt. Mit dem app-Verzeichnis wurde diese Einschränkung aufgehoben. Globale Styles können zu jedem Layout, jeder Seite oder jeder Komponente hinzugefügt werden.

Tailwind CSS

Wenn Sie Tailwind CSS verwenden, müssen Sie das app-Verzeichnis zu Ihrer tailwind.config.js-Datei hinzufügen:

tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Diese Zeile hinzufügen
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}

Sie müssen auch Ihre globalen Styles in Ihrer app/layout.js-Datei importieren:

app/layout.js
import '../styles/globals.css'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

Erfahren Sie mehr über Styling mit Tailwind CSS

Verwendung von App Router zusammen mit Pages Router

Beim Navigieren zwischen Routen, die von den verschiedenen Next.js-Routern bereitgestellt werden, erfolgt eine Hard Navigation. Die automatische Link-Vorabrufung mit next/link funktioniert nicht routerübergreifend.

Stattdessen können Sie Navigationen optimieren zwischen App Router und Pages Router, um die vorabgerufenen und schnellen Seitenübergänge beizubehalten. Mehr erfahren.

Codemods

Next.js bietet Codemod-Transformationen, um bei der Aktualisierung Ihres Codebase zu helfen, wenn eine Funktion veraltet ist. Weitere Informationen finden Sie unter Codemods.