Was ist Next.js?
Next.js ist ein React framework, das eine UI-Library in eine vollständige Applikationsplattform verwandelt. Es ergänzt dateibasierte Routing-Mechanismen, Server Rendering, Data Fetching, API-Endpunkte, Bild- und Schriftoptimierung sowie eine Server-Runtime. Anstatt einen Router, einen Data Layer und ein Build-Setup einzeln auszuwählen und zu konfigurieren, erhalten Sie einen kohärenten Stack, der perfekt aufeinander abgestimmt ist.
Es wird von Vercel gepflegt und hat sich zum Standard für die Entwicklung von produktiven React-Anwendungen entwickelt. Das aktuelle Modell ist der App Router, der auf React Server Components basiert und grundlegend verändert, wo Ihr Code ausgeführt wird und wie der Datenfluss funktioniert.
Der App Router und dateibasiertes Routing
Routes ergeben sich aus dem Dateisystem. Ein Ordner entspricht einem URL-Segment, und eine page.tsx-Datei macht daraus eine Route.
app/
├── layout.tsx # root layout, wraps everything
├── page.tsx # /
├── about/page.tsx # /about
├── blog/
│ ├── page.tsx # /blog
│ └── [slug]/page.tsx # /blog/:slug
└── dashboard/
├── layout.tsx # layout for /dashboard/*
└── page.tsx # /dashboard
Dynamische Segmente werden durch eckige Klammern gekennzeichnet, und Layouts werden verschachtelt, sodass gemeinsam genutzte UI-Elemente während der Navigation gemountet bleiben. Spezielle Dateien wie loading.tsx, error.tsx und not-found.tsx handhaben die entsprechenden Zustände ohne zusätzlichen Code.
// app/blog/[slug]/page.tsx
export default async function PostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return <article>{post.content}</article>;
}
Server- und Client-Components
Dies ist das Konzept, das das moderne Next.js definiert. Standardmäßig sind Components Server Components: Sie werden auf dem Server gerendert, können Daten und Secrets direkt auslesen und fügen nichts zum Browser-Bundle hinzu. Components, die Interaktivität benötigen, opten mit "use client" ein.
// app/counter.tsx
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Eine Server Component kann eine Client Component importieren und rendern, aber nicht umgekehrt. Die Faustregel lautet: Behalte Components auf dem Server und schiebe "use client" so weit wie möglich nach unten an das kleinste interaktive Blatt. So bleibt das JavaScript, das du an den Browser sendest, gering.
Datenabruf
Im App Router rufen Sie Daten in Server Components mit der standardmäßigen fetch-Funktion ab, die um Caching- und Revalidierungsoptionen erweitert wurde.
// app/posts/page.tsx
async function getPosts() {
const res = await fetch("https://api.example.com/posts", {
next: { revalidate: 60 },
});
if (!res.ok) throw new Error("Failed to load posts");
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return <PostList posts={posts} />;
}
Da der Code auf dem Server ausgeführt wird, muss kein Lade-Spinner implementiert werden und es gibt keinen Waterfall auf dem Client. Sie können die Revalidierung zeitgesteuert, bei Bedarf mit revalidatePath oder revalidateTag durchführen oder das Caching komplett deaktivieren. Fetch-Anfragen werden zudem innerhalb eines Render-Vorgangs dedupliziert.
Route Handler und Server Actions
Für API-Endpunkte exportieren Route Handler Funktionen, die ein Web Request empfangen und ein Response zurückgeben.
// app/api/posts/route.ts
export async function GET() {
const posts = await db.post.findMany();
return Response.json(posts);
}
export async function POST(request: Request) {
const body = await request.json();
const post = await db.post.create({ data: body });
return Response.json(post, { status: 201 });
}
Für Mutationen aus deiner eigenen UI sind Server Actions in der Regel einfacher. Markiere eine Funktion mit "use server" und rufe sie aus einem Formular oder einem Event-Handler auf.
// app/posts/new/page.tsx
import { revalidatePath } from "next/cache";
async function createPost(formData: FormData) {
"use server";
await db.post.create({ data: { title: String(formData.get("title")) } });
revalidatePath("/posts");
}
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create</button>
</form>
);
}
Server Actions übernehmen die Validierung, Datenbank-Schreibvorgänge und die Cache-Revalidierung in einem Schritt, wobei das Formular bereits funktioniert, bevor JavaScript geladen wird. Das ist Progressive Enhancement ohne zusätzlichen Aufwand.
Rendering und Caching
Next.js unterstützt mehrere Rendering-Strategien innerhalb einer App:
- Statische Seiten werden zur Build-Zeit vorgerendert.
- Server-gerenderte Seiten werden pro Request generiert.
- Streaming sendet HTML in Chunks, sodass langsame Daten den Shell-Aufbau nicht blockieren.
- Client-Rendering übernimmt die interaktiven Islands nach der Hydration.
Das Caching besteht aus mehreren Ebenen, vom fetch-Cache bis zum vollständigen Route-Cache. Diese zu verstehen, ist die größte Lernhürde, wobei die Standardwerte auf maximale Geschwindigkeit ausgelegt sind. Wenn sich Daten veraltet anfühlen, ist die Lösung in der Regel ein expliziter Revalidation-Aufruf, anstatt das Caching überall zu deaktivieren.
Integrierte Optimierung
Next.js liefert Komponenten mit, die gängige Performance-Aufgaben übernehmen:
next/imagepasst die Größe an, nutzt Lazy-Loading und stellt moderne Formate bereit.next/fonthostet Schriftarten selbst und verhindert Layout-Verschiebungen.next/scriptsteuert das Laden von Drittanbieter-Skripten.- Turbopack beschleunigt die Development- und Production-Builds.
Dank dieser Standardeinstellungen ist eine gut strukturierte Next.js App schnell, ohne dass ein separates Optimierungsprojekt erforderlich ist.
Best Practices
- Behalte Komponenten auf dem Server und nutze
"use client"nur dort, wo es wirklich nötig ist. - Hole Daten in Server Components ab, anstatt Effects zu verwenden.
- Nutze Server Actions für Mutationen und führe eine explizite Revalidierung durch.
- Platziere Routes, Komponenten und den Datenzugriff gemeinsam unter
app/. - Füge
loading.tsxunderror.tsxan Route-Grenzen hinzu, um eine gute UX zu gewährleisten. - Bevorzuge die Image- und Font-Komponenten des Frameworks gegenüber einer manuellen Handhabung.
- Verstehe das Caching, bevor du es deaktivierst; setze stattdessen auf gezielte Revalidierung.
Häufige Fehler
"use client"willkürlich an der Spitze des Baums zu platzieren und dadurch die Server-Vorteile zu verlieren.- Daten in
useEffectabzurufen, obwohl eine Server Component ausreichen würde. - Zu vergessen, nach einer Mutation zu revalidieren, wodurch veraltete Daten angezeigt werden.
- Davon auszugehen, dass die APIs des App Routers und des Pages Routers austauschbar sind.
- Eine Route aufgrund langsamer Daten zu blockieren, anstatt Streaming mit Suspense zu nutzen.
- Next.js lediglich als Front-end-Tool zu betrachten und ein separates Backend neu aufzubauen.
Wie geht es weiter?
Next.js ist derzeit der umfassendste Weg, um React-Anwendungen zu veröffentlichen. Vertiefe deine Grundlagen in React und TypeScript, vergleiche das Modell mit Remix und Astro und lerne die Node.js-Runtime kennen, auf der es deployt wird. Baue anschließend eine kleine Full-Stack-App mit einer Server Component, einem Route Handler und einer Server Action.