Suspense Sınırları ve Bileşen Düzeyinde Akış
Yavaş veri bileşenlerini Suspense içine alarak bunları statik kabuktan bağımsız biçimde akışa verin.
Suspense Sınırları ve Bileşen Düzeyinde Akış, CoddyKit'te ücretsiz bir Next.js 15 Fullstack (App Router + Server Actions) dersidir. Bu, 4 dersinin 1. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Next.js 15 Fullstack (App Router + Server Actions) öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Next.js 15 Fullstack (App Router + Server Actions) kursu toplamda 4 dersten oluşur.
Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.
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.
fallbackshows 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
usehook for parallel, streamed data. loading.tsxis an automatic route-level Suspense for coarse first paint; inline<Suspense>handles fine-grained streaming.
Sıkça Sorulan Sorular
“Suspense Sınırları ve Bileşen Düzeyinde Akış” dersi ücretsiz mi?
Evet — “Suspense Sınırları ve Bileşen Düzeyinde Akış” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Next.js 15 Fullstack (App Router + Server Actions) kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Next.js 15 Fullstack (App Router + Server Actions) kursu toplamda 4 dersten oluşur.
“Suspense Sınırları ve Bileşen Düzeyinde Akış” dersinde ne öğreneceğim?
Yavaş veri bileşenlerini Suspense içine alarak bunları statik kabuktan bağımsız biçimde akışa verin. Next.js 15 Fullstack (App Router + Server Actions) ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Next.js 15 Fullstack (App Router + Server Actions) öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Next.js 15 Fullstack (App Router + Server Actions), başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 1. dersidir.
“Suspense Sınırları ve Bileşen Düzeyinde Akış” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Next.js 15 Fullstack (App Router + Server Actions) dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Next.js 15 Fullstack (App Router + Server Actions) dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Suspense Sınırları ve Bileşen Düzeyinde Akış
- Anlamlı loading.tsx ve İskeletler Oluşturma
- Kısmi Önceden Oluşturma: Statik Kabuk, Dinamik Boşluklar
- Akıştaki Sorunlar: Yerleşim Kayması ve Şelaleler