0Pricing
Next.js 15 Fullstack (App Router + Server Actions) · Leçon

Limites Suspense et flux au niveau des composants

Encapsulez les composants dont les données sont lentes dans Suspense afin de les diffuser indépendamment de la structure statique.

Limites Suspense et flux au niveau des composants est une leçon Next.js 15 Fullstack (App Router + Server Actions) gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Next.js 15 Fullstack (App Router + Server Actions), et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Next.js 15 Fullstack (App Router + Server Actions) comprend 4 leçons au total.

Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.

Why Stream at the Component Level?

In the App Router, a page can contain both fast static content (header, nav, layout shell) and slow data-dependent content (a dashboard widget that hits a third-party API).

Without streaming, the whole page waits for the slowest fetch before anything renders. That hurts perceived performance.

Component-level streaming lets Next.js send the static shell immediately, then stream each slow part in as its data resolves. The tool that enables this is React's <Suspense> boundary.

The Blocking Problem

Here a single async Server Component awaits a slow query. Because the page awaits before returning JSX, the user sees nothing until the 2-second fetch finishes.

The fast parts of the page (title, layout) are held hostage by the slow part.

// app/dashboard/page.tsx
async function getStats() {
  // simulates a slow 2s upstream call
  await new Promise((r) => setTimeout(r, 2000));
  return { revenue: 4200, orders: 87 };
}

export default async function DashboardPage() {
  const stats = await getStats(); // blocks the WHOLE page
  return (
    <main>
      <h1>Dashboard</h1>
      <p>Revenue: {stats.revenue}</p>
      <p>Orders: {stats.orders}</p>
    </main>
  );
}

Extract the Slow Part into Its Own Component

The first step to streaming is isolation: move the awaited data into a separate async Server Component.

The page itself no longer awaits anything, so its static shell can render instantly. The slow work now lives inside <Stats />.

// app/dashboard/stats.tsx
async function getStats() {
  await new Promise((r) => setTimeout(r, 2000));
  return { revenue: 4200, orders: 87 };
}

export async function Stats() {
  const stats = await getStats();
  return (
    <section>
      <p>Revenue: {stats.revenue}</p>
      <p>Orders: {stats.orders}</p>
    </section>
  );
}

Wrap It in a Suspense Boundary

Now wrap the slow component in <Suspense> and give it a fallback. Next.js renders the shell plus the fallback immediately, then streams the real component over the same HTTP response once its data resolves.

  • fallback shows while the boundary's data is pending.
  • Everything outside the boundary is sent right away.
// app/dashboard/page.tsx
import { Suspense } from 'react';
import { Stats } from './stats';

export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1> {/* sent immediately */}
      <Suspense fallback={<p>Loading stats…</p>}>
        <Stats /> {/* streamed in when ready */}
      </Suspense>
    </main>
  );
}

A Good Fallback Is a Skeleton

The fallback should match the shape of the final content to avoid layout shift. A skeleton placeholder is far better than a bare spinner because it reserves space and signals what's coming.

Keep skeletons as plain, fast Client or Server Components with no data dependencies.

// app/dashboard/stats-skeleton.tsx
export function StatsSkeleton() {
  return (
    <section aria-hidden className="animate-pulse">
      <div className="h-6 w-40 rounded bg-gray-200" />
      <div className="mt-2 h-6 w-32 rounded bg-gray-200" />
    </section>
  );
}

// usage:
// <Suspense fallback={<StatsSkeleton />}>
//   <Stats />
// </Suspense>

Multiple Independent Boundaries

Each <Suspense> streams independently. If you have several slow widgets, give each its own boundary so a slow one never blocks a fast one.

Below, RecentOrders may resolve in 300ms while Revenue takes 2s — and each appears the moment it's ready, in any order.

import { Suspense } from 'react';
import { Revenue } from './revenue';
import { RecentOrders } from './recent-orders';
import { RevenueSkeleton, OrdersSkeleton } from './skeletons';

export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1>
      <Suspense fallback={<RevenueSkeleton />}>
        <Revenue />
      </Suspense>
      <Suspense fallback={<OrdersSkeleton />}>
        <RecentOrders />
      </Suspense>
    </main>
  );
}

Boundary Granularity Is a Design Choice

You decide how to group slow components under boundaries:

  • One boundary per widget → each widget pops in on its own (best for unrelated data).
  • One boundary around a group → the group appears together once all its data resolves (good when a coordinated reveal looks cleaner).

A shared boundary streams only when the slowest child inside it is ready, so don't accidentally couple a fast widget to a slow one.

Passing Promises Down with the `use` Hook

An alternative pattern: start the fetch in the parent without awaiting, then pass the promise to a child that unwraps it with React's use hook. The child must be inside a <Suspense> boundary, which suspends on the pending promise.

This lets the parent kick off several requests in parallel before any of them block.

// app/dashboard/page.tsx
import { Suspense } from 'react';
import { Stats } from './stats';

function getStats() {
  return new Promise<{ revenue: number }>((r) =>
    setTimeout(() => r({ revenue: 4200 }), 2000),
  );
}

export default function Page() {
  const statsPromise = getStats(); // NOT awaited
  return (
    <Suspense fallback={<p>Loading…</p>}>
      <Stats statsPromise={statsPromise} />
    </Suspense>
  );
}

The Client Component That Reads the Promise

The child uses use(promise) to read the resolved value. When the promise is pending, use suspends and the nearest <Suspense> shows its fallback.

use can be called in a Client Component, making it the idiomatic way to stream a server-started promise into interactive UI.

// app/dashboard/stats.tsx
'use client';
import { use } from 'react';

export function Stats({
  statsPromise,
}: {
  statsPromise: Promise<{ revenue: number }>;
}) {
  const stats = use(statsPromise); // suspends until resolved
  return <p>Revenue: {stats.revenue}</p>;
}

loading.tsx Is a Route-Level Suspense

A file named loading.tsx in a route segment is sugar: Next.js automatically wraps that segment's page.tsx in a <Suspense> using the loading file as the fallback.

  • loading.tsx → streams the whole page while it loads (one big boundary).
  • Manual <Suspense> → streams parts of the page independently.

Use loading.tsx for the coarse first paint, and inline <Suspense> for fine-grained component streaming inside the page.

// app/dashboard/loading.tsx
export default function Loading() {
  return <p>Loading dashboard…</p>;
}

Don't Forget: Pure Functions Can Be Tested in Isolation

The data-shaping logic that feeds your streamed components is just plain TypeScript — keep it pure so you can unit-test it without a server. Here a standalone summarizer that any judge can run.

type Order = { id: number; total: number };

function summarize(orders: Order[]): { count: number; revenue: number } {
  const revenue = orders.reduce((sum, o) => sum + o.total, 0);
  return { count: orders.length, revenue };
}

const orders: Order[] = [
  { id: 1, total: 1200 },
  { id: 2, total: 3000 },
];

const result = summarize(orders);
console.log(`Orders: ${result.count}, Revenue: ${result.revenue}`);

Quick Check

You have a dashboard page with a fast header and two slow widgets: <Revenue /> (~2s) and <Orders /> (~300ms). You want the header to appear instantly and each widget to appear the moment its own data is ready, independently.

Recap

Key takeaways for component-level streaming:

  • Isolate slow data fetches into their own async Server Components.
  • Wrap each in <Suspense fallback={…}> — the shell and everything outside the boundary stream immediately.
  • Use skeleton fallbacks that match final shape to avoid layout shift.
  • Multiple boundaries stream independently; a shared boundary waits for its slowest child.
  • Pass an un-awaited promise down and read it with the use hook for parallel, streamed data.
  • loading.tsx is an automatic route-level Suspense for coarse first paint; inline <Suspense> handles fine-grained streaming.

Questions Fréquemment Posées

La leçon « Limites Suspense et flux au niveau des composants » est-elle gratuite ?

Oui — le texte complet de « Limites Suspense et flux au niveau des composants » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Next.js 15 Fullstack (App Router + Server Actions), passe à CoddyKit PRO. Le cours Next.js 15 Fullstack (App Router + Server Actions) comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Limites Suspense et flux au niveau des composants » ?

Encapsulez les composants dont les données sont lentes dans Suspense afin de les diffuser indépendamment de la structure statique. Tu pratiques Next.js 15 Fullstack (App Router + Server Actions) avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Next.js 15 Fullstack (App Router + Server Actions) ?

Aucune expérience préalable n'est requise. Next.js 15 Fullstack (App Router + Server Actions) sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Limites Suspense et flux au niveau des composants » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Next.js 15 Fullstack (App Router + Server Actions) ?

Oui. Chaque leçon Next.js 15 Fullstack (App Router + Server Actions) inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Limites Suspense et flux au niveau des composants
  2. Concevoir des écrans de chargement et des squelettes pertinents avec loading.tsx
  3. Pré-rendu partiel : structure statique et zones dynamiques
  4. Pièges du flux : décalages de mise en page et cascades
← Retour à Next.js 15 Fullstack (App Router + Server Actions)