Statisch oder dynamisch
Wann eine Seite beim Bauen entsteht – und wann bei jedem Aufruf
Next.js entscheidet selbst, ob eine Seite statisch oder dynamisch ist – anhand dessen, was du benutzt. Wer das weiß, steuert es absichtlich statt zufällig.
Es gibt keinen Schalter „diese Seite bitte statisch“. Next.js schaut sich an, was deine Seite braucht, und leitet daraus ab, wann sie erzeugt werden kann. Diese Lektion macht die Ableitung sichtbar.
Die drei Möglichkeiten
Statisch einmal beim Bauen erzeugt, für alle gleich
→ schnellstmöglich, kostet fast nichts
Startseite, Blogartikel, Dokumentation
Statisch + Zeit wie oben, aber mit Verfallsdatum (revalidate)
→ aktuell genug, ohne pro Aufruf zu arbeiten
Preisliste, Nachrichtenübersicht
Dynamisch bei jeder Anfrage neu erzeugt
→ nötig, sobald es persönlich wird
Konto, Warenkorb, Dashboard Statisch ist ein gedrucktes Plakat: einmal produziert, tausendfach aufgehängt. Dynamisch ist ein handgeschriebener Zettel pro Person. Und revalidate ist das Plakat, das jeden Morgen neu gedruckt wird.
Woran Next.js es festmacht
Statisch bleibt die Seite, solange nichts davon vorkommt:
cookies() → braucht die konkrete Anfrage
headers() → dito
searchParams → dito
no-store beim fetch
export const dynamic = "force-dynamic"
Fehlt all das, kann Next.js die Seite beim Bauen fertigstellen. npm run build listet jede Adresse mit einem Symbol: ○ statisch, ƒ dynamisch, ● beim Bauen aus generateStaticParams erzeugt. Wenn dort etwas anderes steht als erwartet, hast du die Ursache gefunden, bevor du eine Zeile Code liest.
Dynamische Adressen statisch machen
Eine Seite mit [slug] ist zunächst dynamisch – Next.js kennt die möglichen Werte ja nicht. Mit generateStaticParamsSagt Next.js beim Bauen, welche konkreten Werte ein dynamisches Segment annehmen soll – damit daraus statische Seiten werden.→ nennst du sie, und aus einer Adresse werden viele fertige Seiten.
export async function generateStaticParams() {
const artikel = await ladeAlleArtikel();
return artikel.map((a) => ({ slug: a.slug }));
}
export default async function Seite({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const artikel = await ladeArtikel(slug);
return <article>{artikel.inhalt}</article>;
} Ruft jemand einen Slug auf, der nicht in der Liste stand, erzeugt Next.js die Seite beim ersten Zugriff und merkt sie sich danach. Willst du das nicht, setzt du export const dynamicParams = false – dann gibt es für alles Übrige eine 404.
Der häufigste Fehler
export default async function Startseite() {
const nutzer = await ausCookieLesen(); // macht ALLES dynamisch
const artikel = await ladeArtikel();
return (
<>
<Begruessung nutzer={nutzer} />
<ArtikelListe artikel={artikel} />
</>
);
} export default async function Startseite() {
const artikel = await ladeArtikel(); // bleibt statisch
return (
<>
<Suspense fallback={<BegruessungSkelett />}>
<Begruessung /> {/* liest das Cookie selbst */}
</Suspense>
<ArtikelListe artikel={artikel} />
</>
);
} Die dynamische Abhängigkeit steckt jetzt in einer eigenen Komponente hinter <Suspense>. Der Rest der Seite kann weiterhin beim Bauen entstehen.
Bewusst steuern
// in page.tsx oder layout.tsx – gilt für alles darunter
export const dynamic = "force-dynamic"; // immer frisch
export const dynamic = "force-static"; // immer statisch (Fehler bei cookies())
export const revalidate = 3600; // statisch mit Verfallsdatum Es ist verlockend, wenn etwas nicht aktuell wird. Aber damit verzichtest du auf den größten Vorteil des Frameworks. Erst prüfen, ob nicht revalidate oder ein gezieltes revalidateTag genügt.
▸ Genauer erklärt: was beim Bauen wirklich passiert optional
Route (app) Size First Load JS
┌ ○ / 1.2 kB 92 kB
├ ● /blog/[slug] 850 B 91 kB
│ ├ /blog/hallo-welt
│ └ /blog/next-15
├ ƒ /konto 2.1 kB 95 kB
└ ○ /impressum 180 B 88 kB
○ statisch beim Bauen erzeugt
● SSG aus generateStaticParams erzeugt
ƒ dynamisch bei jeder Anfrage Zwei Spalten lohnen den Blick. Das Symbol zeigt, ob deine Absicht aufgegangen ist. Und „First Load JS“ ist das JavaScript, das der Nutzer laden muss, bevor die Seite reagiert – steigt dieser Wert plötzlich, ist meist eine Client-Grenze zu weit oben gerutscht.
Es lohnt sich, die Bauausgabe bei größeren Änderungen kurz anzusehen. Eine Seite, die unbemerkt von ○ auf ƒ gewechselt ist, kostet ab dem Deploy bei jedem Aufruf Rechenzeit – und fällt sonst erst durch die Rechnung auf.
Sitzt das schon?
5 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.