loading.tsx ist kein Spinner
Eine Loading-Datei ist eine Suspense-Grenze um ein ganzes Routensegment - sie entscheidet also, wie viel Ihrer Seite auf Ihre langsamste Abfrage wartet. Die meisten liegen aus Versehen dort.
Eine loading.tsx in eine Route zu legen gibt Ihnen sofort einen Ladezustand,
und dort hören die meisten Teams auf zu lesen. Die Datei ist keine
Spinner-Komponente. Sie ist der Fallback einer Suspense-Grenze, die Next.js um
das gesamte Segment legt - und die Grenze ist der entscheidende Teil.
Praktisch heißt das: alles innerhalb des Segments wartet auf das Langsamste
darin. Eine loading.tsx ganz oben in einer Dashboard-Route macht aus einer
Seite mit schnellem Header, schneller Sidebar und einem langsamen Chart eine
Seite, die nichts zeigt, bis das Chart fertig ist.
Header und Sidebar waren nach 40 ms bereit. Der Besucher sah zwei Sekunden lang ein Skelett, weil die Grenze um alle drei gezogen war.
Wohin die Grenze gehört
Um das Langsame, nicht um die Seite.
// app/dashboard/page.tsx - keine loading.tsx in diesem Verzeichnis.
import { Suspense } from 'react';
export default function Dashboard() {
return (
<>
<Header /> {/* rendert sofort */}
<Sidebar /> {/* rendert sofort */}
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart /> {/* das Einzige, das wartet */}
</Suspense>
</>
);
}Die Hülle streamt zuerst, das Chart kommt, wenn es kommt, und der Besucher hat in der Zwischenzeit etwas zu lesen und etwas zum Klicken. Dieselbe Gesamtzeit, völlig andere Erfahrung - und ein deutlich besserer LCP, weil das größte gemalte Element kein Skelett mehr ist.
Wann eine Loading-Datei auf Routenebene richtig ist
Sie ist nicht immer falsch. Nehmen Sie eine, wenn das ganze Segment wirklich von einem einzigen Fetch abhängt - eine Artikelseite, die ohne den Artikel keine Seite ist, eine Detailansicht, die ein Datensatz ist. Dort ist ein Fallback auf Routenebene ehrlich: Es gibt tatsächlich noch nichts zu zeigen.
Der Test ist eine Frage: gibt es auf dieser Seite irgendetwas, das ich vor den Daten rendern könnte? Wenn ja, liegt die Grenze falsch.
Zwei Details, die einen Nachmittag kosten
Eine loading.tsx gilt für ihr Segment und jedes Segment darunter. Eine
Datei unter app/loading.tsx ist der Fallback für die gesamte Anwendung, was
fast nie gemeint war, als jemand sie beim Arbeiten an einer Route anlegte.
Und der Fallback muss den Platz reservieren, den sein Inhalt einnehmen wird. Ein 120 px hohes Skelett vor einem 400 px hohen Chart ist ein Layout-Shift von 280 px in dem Moment, in dem die Daten ankommen - und CLS ist es egal, dass der Sprung Ihr eigener Ladezustand war.
function ChartSkeleton() {
// Gleiche Höhe wie das Chart, damit sich beim Tausch nichts bewegt.
return <div className="h-[400px] animate-pulse rounded-xl bg-ink-100" />;
}Das ist alles. loading.tsx ist eine Bequemlichkeit für einen Fall, und
<Suspense> das Werkzeug für jeden anderen - was man wissen sollte, bevor die
Bequemlichkeit die Entscheidung für einen trifft.
