Next.js 15 fullstack (App Router + Server Actions) · Lektion

Skapa meningsfulla loading.tsx-filer och skelett

Bygg laddningstillstånd på routenivå och skelettplatshållare som matchar den slutliga layouten för att undvika layoutförskjutningar.

Lektion 2 av 413 steg

Skapa meningsfulla loading.tsx-filer och skelett är en gratis lektion i Next.js 15 fullstack (App Router + Server Actions) på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Next.js 15 fullstack (App Router + Server Actions), och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Next.js 15 fullstack (App Router + Server Actions) innehåller totalt 4 lektioner.

Varför loading.tsx finns

I App Router är en fil med namnet loading.tsx speciell: Next.js omsluter automatiskt route-segmentets page.tsx med en React-<Suspense>-gräns och visar loading.tsx som reservinnehåll medan serverkomponenten strömmas.

  • Ni får ett omedelbart laddningstillstånd med noll manuell Suspense-konfiguration.
  • Det aktiveras endast vid rendering på servern av segmentet, inte vid varje interaktion på klienten.
  • Reservinnehållet visas direkt medan sidans asynkrona data blir klar.

Den här lektionen handlar om att göra reservinnehållet meningsfullt, så att användaren upplever sidan som snabb och layouten inte hoppar.

Filkonventionen

Lägg en loading.tsx bredvid page.tsx i valfritt route-segment. Dess standardexport renderas som Suspense-reservinnehåll för segmentet och allt under det.

  • Det är en vanlig React-komponent — den kan vara en Server Component (standard), eftersom den endast renderar statisk markup.
  • Inga props skickas till den; den måste vara fristående.
  • Håll den lättviktig så att den skickas och renderas snabbt.
// app/dashboard/loading.tsx
export default function Loading() {
  return (
    <div className="dashboard-grid" aria-busy="true">
      <DashboardSkeleton />
    </div>
  );
}

Det verkliga målet: undvik layoutförskjutningar

Ett dåligt laddningstillstånd ersätter sidan med en centrerad laddningsindikator och hoppar sedan till en helt annan layout när datan kommer. Det visuella hoppet försämrar den upplevda prestandan och Cumulative Layout Shift (CLS).

Ett bra skelett speglar den slutliga layouten:

  • Samma antal kort, rader och kolumner.
  • Ungefär samma bredd och höjd som det riktiga innehållet.
  • Samma avstånd och rutnätsstruktur.

När den riktiga datan ersätter platshållarna flyttar sig ingenting — endast de grå platshållarna fylls med innehåll.

En återanvändbar skelett-primitive

Börja med en enda liten byggsten: en Skeleton-ruta som renderar en grå, rundad rektangel. Allt annat bygger Ni genom att komponera med den.

  • Acceptera className så att anropande kod kan styra bredd, höjd och form.
  • Lägg till stilen animate-pulse (Tailwind) eller en CSS-shimmer-effekt för att skapa en laddningskänsla.
  • Märk den med aria-hidden — den är dekorativ och inte faktiskt innehåll.
// components/skeleton.tsx
export function Skeleton({ className = '' }: { className?: string }) {
  return (
    <div
      aria-hidden="true"
      className={`animate-pulse rounded-md bg-gray-200 ${className}`}
    />
  );
}

Matcha den slutliga layouten

Bygg skeletonet genom att kopiera strukturen från den riktiga komponenten och sedan ersätta text och bilder med Skeleton-rutor i motsvarande storlek.

  • En avatar blir en cirkel: h-10 w-10 rounded-full.
  • En rubrikrad blir ett brett fält och en underrubrik blir ett kortare, tunnare fält.
  • Använd samma containerklasser (utfyllnad, mellanrum och kantlinje) som i det riktiga kortet.
// components/user-card-skeleton.tsx
import { Skeleton } from './skeleton';

export function UserCardSkeleton() {
  return (
    <div className="flex items-center gap-4 rounded-lg border p-4">
      <Skeleton className="h-10 w-10 rounded-full" />
      <div className="flex-1 space-y-2">
        <Skeleton className="h-4 w-1/3" />
        <Skeleton className="h-3 w-1/2" />
      </div>
    </div>
  );
}

Listor: upprepa radskeletonet

För listor och tabeller renderar Ni ett fast antal radskeleton — tillräckligt många för att fylla den typiska visningsytan så att sidan ser komplett ut.

  • Återanvänd skeletonet för en enskild rad i en array.
  • Välj ett antal som ungefär motsvarar den vanliga sidstorleken (t.ex. 6–8 rader).
  • Behåll samma gap och omslutande element som i den riktiga listan för att bevara avstånden.
// components/user-list-skeleton.tsx
import { UserCardSkeleton } from './user-card-skeleton';

export function UserListSkeleton({ rows = 6 }: { rows?: number }) {
  return (
    <div className="space-y-3">
      {Array.from({ length: rows }).map((_, i) => (
        <UserCardSkeleton key={i} />
      ))}
    </div>
  );
}

Koppla in skeletonet i loading.tsx

Nu komponerar loading.tsx bara Era skeleton-komponenter i samma yttre layout som sidan. Sidhuvudet, som är statiskt (inte databeroende), kan renderas på riktigt även i laddningstillståndet.

  • Rendera de riktiga delarna som visas direkt (sidrubrik och flikar).
  • Byt bara ut de databundna områdena mot skeleton.
  • Det gör att sidan redan känns delvis laddad.
// app/dashboard/users/loading.tsx
import { UserListSkeleton } from '@/components/user-list-skeleton';

export default function Loading() {
  return (
    <section className="mx-auto max-w-2xl p-6">
      <h1 className="mb-4 text-2xl font-bold">Users</h1>
      <UserListSkeleton rows={6} />
    </section>
  );
}

loading.tsx jämfört med Suspense på komponentnivå

loading.tsx täcker hela segmentet — om någon data på sidan är långsam visas hela reservvyn. Ibland behöver Ni mer detaljerad kontroll så att snabbt innehåll renderas först.

  • Använd loading.tsx för den första renderingen som gäller hela segmentet.
  • Omslut enskilda långsamma komponenter med <Suspense> och en egen skeleton-reservvy så att de kan strömmas oberoende av varandra.
  • Kombinera båda: ett enkelt segmentskeleton plus detaljerad Suspense för den långsammaste widgeten.
// app/dashboard/page.tsx
import { Suspense } from 'react';
import { RevenueChart } from './revenue-chart';
import { ChartSkeleton } from './chart-skeleton';

export default function Page() {
  return (
    <main className="p-6">
      <h1 className="text-2xl font-bold">Overview</h1>
      {/* Static + fast content paints immediately */}
      <Suspense fallback={<ChartSkeleton />}>
        {/* Slow async server component streams in later */}
        <RevenueChart />
      </Suspense>
    </main>
  );
}

Ren funktion för antal skeleton

Hur många skeletonrader bör Ni visa? En liten hjälpfunktion håller antalet konsekvent och begränsar det till ett rimligt intervall, så att Ni aldrig renderar ett orimligt antal platshållare.

Det här är vanlig TypeScript-logik — inget ramverk är inblandat — så den kan enhetstestas separat.

function skeletonRowCount(pageSize: number, viewportRows = 8): number {
  if (!Number.isFinite(pageSize) || pageSize <= 0) return viewportRows;
  return Math.min(pageSize, viewportRows);
}

console.log(skeletonRowCount(20));  // 8  (clamped to viewport)
console.log(skeletonRowCount(3));   // 3  (fewer items than viewport)
console.log(skeletonRowCount(0));   // 8  (fallback default)
console.log(skeletonRowCount(-5));  // 8  (guards invalid input)

Tillgänglighet och reducerad rörelse

Skeleton är dekorativa, men de påverkar ändå hjälpmedel och användare som är känsliga för rörelser.

  • Märk laddningsområdet med aria-busy="true" och enskilda platshållare med aria-hidden="true".
  • Tillhandahåll en visuellt dold status, till exempel <span className="sr-only">Loading users</span>, för skärmläsare.
  • Respektera prefers-reduced-motion så att pulsanimeringen inte körs för användare som har valt bort den.
/* globals.css */
@media (prefers-reduced-motion: reduce) {
  .animate-pulse {
    animation: none;
  }
}

Vanliga fallgropar

Undvik följande misstag, som motverkar syftet med ett skeleton:

  • Reservvy med endast en spinner: ger ingen ledtråd om layouten och leder garanterat till ett skifte när innehållet anländer.
  • Felaktiga storlekar: om skeletonkortet är 60px högt men det riktiga kortet är 96px hoppar sidan fortfarande.
  • Glömd omslutning: olika utfyllnad eller rutnät mellan skeletonet och sidan flyttar allt.
  • Skeleton för snabb data: om ett segment laddas på <100ms kan skeletonet blinka till och kännas sämre — överväg Suspense på komponentnivå enbart för de långsamma delarna.

Snabbtest

Testa Era kunskaper om laddningstillstånd och skeleton på routenivå.

Sammanfattning

Ni har lärt Er att utforma meningsfulla laddningstillstånd i App Router:

  • loading.tsx är en automatisk Suspense-reservvy för sitt routsegment.
  • Bygg skeleton från en liten Skeleton-primitiv och komponera dem så att de speglar den slutliga layouten.
  • Matcha omslutningar, antal och dimensioner för att undvika layoutskifte (CLS).
  • Rendera statiska delar som visas direkt (rubriker och flikar) på riktigt; använd skeleton enbart för de databundna områdena.
  • Använd loading.tsx för den första renderingen av hela segmentet och <Suspense> på komponentnivå för att strömma långsamma widgetar oberoende av varandra.
  • Hantera tillgänglighet med aria-busy, aria-hidden, en sr-only-status och prefers-reduced-motion.
Gratis att börja

Lär dig TypeScript med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”Skapa meningsfulla loading.tsx-filer och skelett” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Next.js 15 fullstack (App Router + Server Actions), inklusive ”Skapa meningsfulla loading.tsx-filer och skelett”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Next.js 15 fullstack (App Router + Server Actions) innehåller totalt 4 lektioner.

Vad lär jag mig i ”Skapa meningsfulla loading.tsx-filer och skelett”?

Bygg laddningstillstånd på routenivå och skelettplatshållare som matchar den slutliga layouten för att undvika layoutförskjutningar. Ni övar på Next.js 15 fullstack (App Router + Server Actions) med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Next.js 15 fullstack (App Router + Server Actions)?

Du behöver inga förkunskaper. Utbildningen i Next.js 15 fullstack (App Router + Server Actions) på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Skapa meningsfulla loading.tsx-filer och skelett”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Next.js 15 fullstack (App Router + Server Actions)-lektionen?

Ja. Varje Next.js 15 fullstack (App Router + Server Actions)-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Suspense-gränser och strömning på komponentnivå
  2. Skapa meningsfulla loading.tsx-filer och skelett
  3. Partiell förhandsrendering: statiskt skal, dynamiska luckor
  4. Fallgropar vid strömning: layoutförskjutning och vattenfall
← Tillbaka till Next.js 15 fullstack (App Router + Server Actions)