Design wechseln

Next.js Loading-Zustandsverwaltung: loading.tsx und Suspense – Praxisleitfaden

Easton editorial illustration: hydration gauge console

Kennen Sie das? Ein Nutzer klickt auf einen Link – und drei Sekunden lang bleibt der Bildschirm weiß, ohne jede Rückmeldung. Die Gedanken kreisen: „Ist das abgestürzt?” Dann wird wild F5 gedrückt – und gerade als die Seite fertig geladen ist, wird alles wieder neu geladen …

Früher habe ich Loading genauso gelöst. Bei jeder neuen Seite kam in die Komponente:

const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);

useEffect(() => {
  setLoading(true);
  fetchData()
    .then(setData)
    .finally(() => setLoading(false));
}, []);

if (loading) return <Spinner />;

Der Code war lang und repetitiv – und auf jeder Seite von vorn. Schlimmer noch: Im Team war jede Loading-Logik anders – mal globaler State, mal Context. Wartung wurde zum Albtraum.

Bis ich in der Next.js-Dokumentation entdeckte: Next.js bringt längst eine elegantere Lösung mitloading.tsx und Suspense.

Seitdem ist Loading-Verwaltung erstaunlich einfach: weniger Code, besseres Nutzererlebnis. In diesem Artikel teile ich die Praxiserfahrung damit.

Warum loading.tsx und Suspense?

Schwächen klassischer Ansätze

Ein konkretes Beispiel: eine Blog-Listen-Seite, klassisch geschrieben:

// app/blog/page.tsx
'use client';
import { useState, useEffect } from 'react';

export default function BlogPage() {
  const [loading, setLoading] = useState(true);
  const [posts, setPosts] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    fetch('/api/posts')
      .then(res => res.json())
      .then(data => {
        setPosts(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err);
        setLoading(false);
      });
  }, []);

  if (loading) {
    return <div className="spinner">Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

Sieht akzeptabel aus – aber:

  1. Code-Redundanz: Dieselbe State-Logik auf jeder Seite
  2. Zersplitterter State: loading, data, error in drei States – leicht desynchron
  3. Nur Client Component: useState und useEffect erzwingen Client-Rendering – SSR-Vorteile gehen verloren
  4. Schlechtes UX: Zwischen Klick und sichtbarem Loading oft spürbarer weißer Bildschirm

Beim Code Review sieht man alles Mögliche: Loading in Context, Zustand global, oder pro Komponente ad hoc. In großen Projekten wird das unhaltbar.

Die Next.js-Lösung

Der App Router bietet drei Kernmechanismen:

1. loading.tsx – Convention over Configuration

Eine loading.tsx im Routenordner – Next.js nutzt sie automatisch als Loading-UI. Kein useState, kein manuelles Suspense-Wrapping.

2. Suspense – nativ in React 18

Suspense steuert Loading auf Komponentenebene. Langsame Teile bekommen eine Grenze – der Rest rendert weiter, ohne die ganze Seite zu blockieren.

3. Streaming – schrittweise Anzeige

Mit Streaming erscheint die Seite in Teilen: zuerst Header, dann Sidebar, zuletzt langsame Daten. Kein starres Warten auf Weiß.

Mit Skeleton Screens und Streaming sinken FCP (First Contentful Paint) und LCP (Largest Contentful Paint) spürbar – PageSpeed Insights verbessert sich oft um mehrere Punkte.

loading.tsx – Grundlagen

Schnellstart: die erste loading.tsx

Direkt zur Sache – die einfachste loading.tsx.

Verzeichnisstruktur:

app/
  blog/
    page.tsx

Einfach loading.tsx in blog/ anlegen:

app/
  blog/
    loading.tsx  ← neu
    page.tsx

Inhalt:

// app/blog/loading.tsx
export default function Loading() {
  return (
    <div className="flex items-center justify-center min-h-screen">
      <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900"></div>
      <p className="ml-4">Wird geladen …</p>
    </div>
  );
}

Zehn Zeilen – fertig. Beim Aufruf von /blog zeigt Next.js diese Komponente, bis page.tsx bereit ist.

Wichtig: Sie müssen Suspense nicht manuell wrappen. Next.js packt page.tsx intern in <Suspense fallback={<Loading />}>.

Beim ersten Mal dachte ich: „Zu einfach, funktioniert das?” – ja, und der Code wird deutlich schlanker.

Geltungsbereich von loading.tsx

Entscheidend ist der Begriff Route Segment: loading.tsx gilt für page.tsx im selben Ordner und alle Unterrouten.

app/
  blog/
    loading.tsx     ← gilt für /blog und /blog/[id]
    page.tsx        ← /blog Listen-Seite
    [id]/
      page.tsx      ← /blog/123 Detail-Seite

Angezeigt wird sie bei:

  • Navigation zu /blog
  • Wechsel von der Liste zu /blog/123

Layouts bleiben unberührt. Steht in blog/layout.tsx eine Navigation, bleibt sie sichtbar – nur der page.tsx-Inhalt wird durch Loading ersetzt.

Das ist „Shared Layouts bleiben interaktiv”: Nutzer können während des Ladens woanders navigieren, statt die ganze UI einzufrieren.

Layout (dauerhaft sichtbar)
  ├─ Navigation
  └─ Suspense Boundary
       ├─ Loading UI (während Daten laden)
       └─ Page (nach dem Laden)

Server Component vs. Client Component

loading.tsx ist standardmäßig eine Server Component – meist reicht JSX.

Für Animationen (Framer Motion) oder clientseitige Bibliotheken: 'use client' setzen:

// app/blog/loading.tsx
'use client';
import { motion } from 'framer-motion';

export default function Loading() {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      className="flex items-center justify-center min-h-screen"
    >
      <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900"></div>
    </motion.div>
  );
}

Meine Regel: Server Component, solange möglich – weniger Client-Bundle, schnellerer First Load.

Skeleton Screens in der Praxis

Warum Skeleton besser ist als ein Spinner

Ein drehender Spinner ist vertraut – aus UX-Sicht sind Skeleton Screens oft überlegen.

Psychologisch: Bei Skeletons erwartet das Gehirn „Inhalt kommt gleich” – die wahrgenommene Wartezeit sinkt. Beim Spinner weiß man nur „es lädt”, nicht was oder wie lange – mehr Unruhe.

Skeletons zeigen außerdem die grobe Layout-Struktur: drei Balken suggerieren drei Artikel – Erwartungen sind gesetzt.

Drei Implementierungswege

Variante 1: Reines CSS (am leichtesten)

// app/blog/loading.tsx
export default function Loading() {
  return (
    <div className="max-w-4xl mx-auto p-6">
      {[1, 2, 3].map((i) => (
        <div key={i} className="mb-8 animate-pulse">
          {/* Titel-Skeleton */}
          <div className="h-8 bg-gray-200 rounded w-3/4 mb-4"></div>
          {/* Auszug-Skeleton */}
          <div className="space-y-2">
            <div className="h-4 bg-gray-200 rounded"></div>
            <div className="h-4 bg-gray-200 rounded w-5/6"></div>
          </div>
          {/* Meta-Skeleton */}
          <div className="flex gap-4 mt-4">
            <div className="h-3 bg-gray-200 rounded w-20"></div>
            <div className="h-3 bg-gray-200 rounded w-24"></div>
          </div>
        </div>
      ))}
    </div>
  );
}

Null Dependencies, beste Performance – etwas mehr Handarbeit beim Styling.

Variante 2: react-loading-skeleton (am schnellsten)

npm install react-loading-skeleton
// app/blog/loading.tsx
'use client';
import Skeleton from 'react-loading-skeleton';
import 'react-loading-skeleton/dist/skeleton.css';

export default function Loading() {
  return (
    <div className="max-w-4xl mx-auto p-6">
      {[1, 2, 3].map((i) => (
        <div key={i} className="mb-8">
          <Skeleton height={32} width="75%" className="mb-4" />
          <Skeleton count={2} />
          <div className="flex gap-4 mt-4">
            <Skeleton width={80} />
            <Skeleton width={100} />
          </div>
        </div>
      ))}
    </div>
  );
}

Praktisch, gute Animation – in kleinen Projekten mein Favorit.

Variante 3: shadcn/ui (am professionellsten)

npx shadcn-ui@latest add skeleton
// app/blog/loading.tsx
import { Skeleton } from '@/components/ui/skeleton';

export default function Loading() {
  return (
    <div className="max-w-4xl mx-auto p-6">
      {[1, 2, 3].map((i) => (
        <div key={i} className="mb-8">
          <Skeleton className="h-8 w-3/4 mb-4" />
          <Skeleton className="h-4 w-full mb-2" />
          <Skeleton className="h-4 w-5/6 mb-4" />
          <div className="flex gap-4">
            <Skeleton className="h-3 w-20" />
            <Skeleton className="h-3 w-24" />
          </div>
        </div>
      ))}
    </div>
  );
}

Passt zum Design System – kein Extra-Styling nötig.

Design-Prinzipien für Skeletons

  1. Layout treffen: Struktur wie der echte Inhalt (Titel, Auszug, Tags → entsprechende Skeleton-Bereiche)
  2. Dezente Animation: Sanftes Pulsieren reicht – zu auffällig lenkt ab
  3. Angemessene Anzahl: 3–5 Einträge reichen meist, nicht den ganzen Viewport füllen

Vollständiges Beispiel: Blog-Listen-Seite

loading.tsx:

// app/blog/loading.tsx
export default function BlogLoading() {
  return (
    <div className="max-w-4xl mx-auto px-4 py-8">
      <div className="h-12 bg-gray-200 rounded w-1/3 mb-8 animate-pulse"></div>

      <div className="space-y-8">
        {[1, 2, 3].map((i) => (
          <article key={i} className="border-b pb-8 animate-pulse">
            <div className="h-8 bg-gray-200 rounded w-3/4 mb-3"></div>
            <div className="space-y-2 mb-4">
              <div className="h-4 bg-gray-200 rounded"></div>
              <div className="h-4 bg-gray-200 rounded w-11/12"></div>
              <div className="h-4 bg-gray-200 rounded w-4/5"></div>
            </div>
            <div className="flex gap-3">
              <div className="h-6 bg-gray-200 rounded-full w-16"></div>
              <div className="h-6 bg-gray-200 rounded-full w-20"></div>
            </div>
          </article>
        ))}
      </div>
    </div>
  );
}

page.tsx als Server Component:

// app/blog/page.tsx
async function getPosts() {
  const res = await fetch('https://api.example.com/posts', {
    cache: 'no-store' // Bei jedem Aufruf neu laden
  });

  if (!res.ok) throw new Error('Failed to fetch posts');

  return res.json();
}

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

  return (
    <div className="max-w-4xl mx-auto px-4 py-8">
      <h1 className="text-4xl font-bold mb-8">Blog-Artikel</h1>

      <div className="space-y-8">
        {posts.map((post) => (
          <article key={post.id} className="border-b pb-8">
            <h2 className="text-2xl font-semibold mb-3">
              <a href={`/blog/${post.slug}`} className="hover:text-blue-600">
                {post.title}
              </a>
            </h2>
            <p className="text-gray-600 mb-4">{post.excerpt}</p>
            <div className="flex gap-3">
              {post.tags.map((tag) => (
                <span key={tag} className="px-3 py-1 bg-gray-100 rounded-full text-sm">
                  {tag}
                </span>
              ))}
            </div>
          </article>
        ))}
      </div>
    </div>
  );
}

page.tsx als async-Funktion mit await – kein useState, kein useEffect. Server Component: kein Extra-Client-Bundle, schnellerer First Paint.

Debugging: React DevTools

Loading blitzt oft zu kurz auf. Tipp mit React DevTools:

  1. React DevTools installieren
  2. Tab „Components” öffnen
  3. <Suspense> finden
  4. Rechtsklick → „Suspend this Suspense boundary”

Loading bleibt sichtbar – Styling in Ruhe anpassen, dann Suspend aufheben.

Hätte mir früher viel Zeit gespart.

Suspense – Fortgeschritten

Suspense-Grenzen manuell setzen

loading.tsx reicht nicht immer. Mehrere unabhängige Datenquellen sollen getrennt laden – dann manuelle Suspense-Grenzen.

Typischer Fehler – Suspense zu tief:

// ❌ Falsch – Suspense zu tief
async function PostList() {
  const posts = await fetchPosts();

  return (
    <Suspense fallback={<Loading />}>  {/* funktioniert so nicht! */}
      <div>
        {posts.map(post => <Post key={post.id} {...post} />)}
      </div>
    </Suspense>
  );
}

Suspense muss höher im Baum stehen, um asynchrone Operationen darunter abzufangen.

Richtig – Suspense im Eltern-Component:

// ✅ Richtig – Suspense im Eltern-Component
export default function BlogPage() {
  return (
    <div>
      <h1>Blog-Artikel</h1>

      <Suspense fallback={<PostListSkeleton />}>
        <PostList />
      </Suspense>
    </div>
  );
}

async function PostList() {
  const posts = await fetchPosts();

  return (
    <div>
      {posts.map(post => <Post key={post.id} {...post} />)}
    </div>
  );
}

Suspense als Schleuse: Solange unten etwas wartet, fallback – dann echter Inhalt.

Dynamische Routen: Sonderfall

Produktdetail /products/[id], Wechsel von id=1 zu id=2 – loading.tsx erscheint oft nicht.

Inhalt springt direkt von A nach B – kein Loading-Übergang.

React optimiert: Gleicher Komponententyp → Instanz wird wiederverwendet, nur Props ändern sich. Suspense suspendiert nicht neu.

Lösung: key am Suspense, damit React eine neue Instanz erzeugt:

// app/products/[id]/page.tsx
import { Suspense } from 'react';

export default function ProductPage({ params }: { params: { id: string } }) {
  return (
    <Suspense key={params.id} fallback={<ProductSkeleton />}>
      <ProductDetail id={params.id} />
    </Suspense>
  );
}

async function ProductDetail({ id }: { id: string }) {
  const product = await fetchProduct(id);

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <span>${product.price}</span>
    </div>
  );
}

<Suspense key={params.id} ...> – bei ID-Wechsel neue Instanz, Loading erscheint wieder.

Halber Tag Debugging – bis ein GitHub-Issue den key-Trick erwähnte. Manchmal ist die Lösung trivial, wenn man sie kennt.

Mehrere Ladezustände koordinieren

Dashboard mit Nutzerinfo, Statistik, Aktivität – zwei Strategien:

Strategie 1: Alles in einem Suspense

export default function Dashboard() {
  return (
    <Suspense fallback={<DashboardSkeleton />}>
      <UserInfo />      {/* API 1 */}
      <Statistics />    {/* API 2 */}
      <RecentActivity /> {/* API 3 */}
    </Suspense>
  );
}

Vorteil: einfach, vollständiger Inhalt auf einmal
Nachteil: langsamste API bestimmt die Wartezeit

Strategie 2: Mehrere Suspense-Grenzen

export default function Dashboard() {
  return (
    <div>
      <Suspense fallback={<UserInfoSkeleton />}>
        <UserInfo />
      </Suspense>

      <Suspense fallback={<StatsSkeleton />}>
        <Statistics />
      </Suspense>

      <Suspense fallback={<ActivitySkeleton />}>
        <RecentActivity />
      </Suspense>
    </div>
  );
}

Vorteil: Schnelles zuerst – kürzere gefühlte Wartezeit
Nachteil: Layout kann „springen”

Meine Wahl nach Wichtigkeit:

  • Kern-Daten (Nutzerinfo) gemeinsam in einem Suspense
  • Sekundäres (Empfehlungen, Ads) in eigenen Suspense-Grenzen

Häufige Probleme und Lösungen

Suspense wirkt nicht

Checkliste:

1. Datenabruf-Methode

Suspense braucht kompatible Patterns im App Router:

  • ✅ await direkt in Server Components (empfohlen)
  • ✅ Suspense-fähige Libraries (SWR, React Query)
  • ❌ fetch in useEffect
  • ❌ klassisches Promise.then ohne Integration

2. Position im Baum

Suspense über der datenladenden Komponente – nicht darin oder darunter.

3. Versionen

  • React 18+
  • Next.js 13+ (App Router)

4. Debuggen

React DevTools: Suspense manuell suspendieren. Keine Reaktion → Suspense greift nicht – obige Punkte prüfen.

Fallstrick: useFormStatus

Mit Server Actions und useFormStatus für Submit-Status:

useFormStatus funktioniert nur in Client Components.

Das Formular sollte in einer Server Component gerendert werden, damit die Server Action gebunden ist.

Pattern: Server Component rendert form, Client Component den Button-Status

// app/actions.ts
'use server';
export async function submitForm(formData: FormData) {
  // Formular verarbeiten …
  await saveToDatabase(formData);
}
// app/page.tsx (Server Component)
import { submitForm } from './actions';
import { SubmitButton } from './submit-button';

export default function Page() {
  return (
    <form action={submitForm}>
      <input name="email" type="email" />
      <SubmitButton />
    </form>
  );
}
// app/submit-button.tsx (Client Component)
'use client';
import { useFormStatus } from 'react-dom';

export function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Wird gesendet …' : 'Absenden'}
    </button>
  );
}

Form in Server Component, Button in Client Component – Server Action und Pending-State funktionieren zusammen.

Prefetch und Loading

Next.js <Link> prefetcht standardmäßig (wenn der Link im Viewport ist).

Dann blitzt Loading kurz oder gar nicht – Daten sind schon da.

Zum Testen Prefetch abschalten:

<Link href="/blog" prefetch={false}>
  Blog
</Link>

In Produktion Prefetch eher an lassen. Bei zu kurzem Loading: Mindestanzeige (z. B. 300 ms) oder Skeleton statt Spinner.

Zusammenfassung

Kernpunkte:

  1. loading.tsx ist Best Practice auf Routenebene – Datei im Routenordner, Next.js erledigt den Rest. Kein manuelles useState, deutlich weniger Code.

  2. Skeleton schlägt Spinner – Layout vorab, weniger Unruhe. CSS, react-loading-skeleton oder UI-Bibliothek – je nach Projekt.

  3. Suspense oben im Baum – Schleuse für asynchrone Kinder. Falsche Position → kein Effekt.

  4. Dynamische Routen: key nicht vergessen – sonst kein Loading bei ID-Wechsel. <Suspense key={params.id}>.

  5. Mehrere Datenquellen: Suspense gezielt splitten – Kern zusammen, Rest asynchron.

Vom manuellen useState zu loading.tsx ist das kein Mehraufwand, sondern smarter arbeiten. Weniger Code, weniger Bugs, besseres UX.

Nächste Schritte

Sofort: Ein bestehendes Projekt, eine einfache Listen-Seite – Loading auf loading.tsx umstellen. Einmal selbst bauen hilft mehr als zehn Artikel.

Vertiefung: Als Nächstes Error Boundaries – Loading und Errors sind ein Paar (error.tsx). Dazu folgt ein eigener Praxisartikel.

Austausch: Wie lösen Sie Loading in Ihren Projekten? Welche Fallstricke? Gerne in den Kommentaren.


Referenzen:

Next.js Loading-Zustandsverwaltung – vollständiger Ablauf

loading.tsx und Suspense für professionelles Ladeerlebnis – ohne manuelles useState

⏱️ Estimated time: 1 hr

  1. 1

    Step 1: loading.tsx anlegen

    loading.tsx im Routenverzeichnis erstellen:
    • app/dashboard/loading.tsx – Loading für die Dashboard-Route
    • app/products/[id]/loading.tsx – Loading für dynamische Routen

    Dateiinhalt:
    export default function Loading() {
    return <div>Wird geladen …</div>
    }

    Next.js zeigt diese Komponente beim Laden automatisch an
  2. 2

    Step 2: Skeleton Screen implementieren

    Professionellere Loading-UI:
    • Skeleton-Komponente im Layout des echten Inhalts
    • Struktur an den finalen Inhalt anpassen
    • Animation für besseres Erlebnis

    Beispiel:
    export default function Loading() {
    return (
    <div className="animate-pulse">
    <div className="h-8 bg-gray-200 rounded w-3/4 mb-4"></div>
    <div className="h-4 bg-gray-200 rounded w-full mb-2"></div>
    <div className="h-4 bg-gray-200 rounded w-5/6"></div>
    </div>
    )
    }
  3. 3

    Step 3: Asynchrone Komponenten mit Suspense wrappen

    Suspense in Komponenten nutzen:
    • Komponenten mit asynchronem Datenabruf wrappen
    • fallback für Loading setzen
    • Verschachtelte Suspense für feingranulares Laden

    Beispiel:
    <Suspense fallback={<Loading />}>
    <AsyncComponent />
    </Suspense>

    Mehrere Komponenten jeweils eigenes Suspense:
    • Unabhängiges Laden
    • Schnelles zuerst, langsames später
    • Besseres Nutzererlebnis
  4. 4

    Step 4: Loading bei dynamischen Routen

    Loading für dynamische Routen:
    • loading.tsx im dynamischen Routenordner
    • Next.js behandelt Parameterwechsel automatisch
    • Kein manuelles Loading-State-Management

    Beispiel:
    app/products/[id]/
    ├── loading.tsx # bei Parameterwechsel automatisch
    └── page.tsx

    Navigation von /products/1 zu /products/2
    zeigt loading.tsx automatisch
  5. 5

    Step 5: Ladeerlebnis optimieren

    Optimierungstipps:
    • Skeleton statt einfachem Spinner
    • Loading-UI an echtes Layout anpassen
    • Animation (animate-pulse) nutzen
    • Suspense für Streaming sinnvoll einsetzen

    Vermeiden:
    • loading.tsx überall blind einsetzen
    • zu komplexe Loading-UI
    • Fehlerbehandlung vergessen (error.tsx ergänzen)
  6. 6

    Step 6: Testen und validieren

    Test-Schwerpunkte:
    • Loading bei Navigation
    • Loading bei dynamischen Parameterwechseln
    • Verhalten bei langsamer Verbindung
    • Flüssigkeit der Loading-UI

    Checkliste:
    • Passende Loading-States für alle Routen
    • Loading-UI entspricht dem Inhaltslayout
    • Kein Flackern oder Layout-Shift
    • Flüssiges Nutzererlebnis

FAQ

Was ist der Unterschied zwischen loading.tsx und manuellem useState?
loading.tsx ist eine Next.js-Konvention: Beim Laden der Seite wird der Zustand automatisch angezeigt, ohne manuelles State-Management. Mit useState muss jede Seite dieselbe Logik wiederholen – fehleranfällig und redundant. loading.tsx reduziert den Code oft um ~50 % und unterstützt Streaming.
Wann wird loading.tsx angezeigt?
Automatisch bei: 1) Navigation zur Route; 2) Parameterwechsel bei dynamischen Routen; 3) Laden übergeordneter Routen. Next.js steuert Timing und Ausblendung – kein manuelles Ein-/Ausblenden nötig.
Was ist der Unterschied zwischen Suspense und loading.tsx?
loading.tsx gilt routenweit für die ganze Seite. Suspense wirkt auf Komponentenebene und erlaubt feingranulare Kontrolle. Beides kombinierbar: loading.tsx für die Route, Suspense für einzelne async-Komponenten.
Wie implementiert man Skeleton Screens?
In loading.tsx Skeleton-Elemente im Layout des echten Inhalts. Tailwind animate-pulse für Animation. Layout des Skeletons an den finalen Inhalt anpassen – vermeidet Layout-Shift nach dem Laden.
Wie behandelt man Loading bei dynamischen Routen?
loading.tsx im Ordner der dynamischen Route, z. B. app/products/[id]/loading.tsx. Beim Wechsel von /products/1 zu /products/2 zeigt Next.js loading.tsx automatisch – ohne manuelles Reagieren auf Parameteränderungen.
Kann man das Loading-Styling anpassen?
Ja. loading.tsx ist eine normale React-Komponente – Tailwind, CSS Modules, styled-components usw. Skeleton statt Spinner wird für UX empfohlen.
Beeinträchtigt loading.tsx die Performance?
Nein – oft verbessert es sie. loading.tsx unterstützt Streaming: Teile der Seite erscheinen früher, Nutzer warten nicht auf den gesamten Page-Load. Schnelle Bereiche zuerst, langsame später.

11 Min. Lesezeit · Veröffentlicht am: 5. Jan. 2026 · Aktualisiert am: 14. Juli 2026

Ähnliche Beiträge

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog