Next.js · Lesson 7 of 15
Streaming and Loading UI
Show shells instantly and stream the rest with Suspense.
- Intermediate
- 15 min read
- 3 objectives
Before this lessonLesson 6: Data Fetching and Caching
What you will learn
- Add loading.tsx
- Split slow parts into Suspense
- Avoid blocking the whole page
Your Progress
0 of 15 lessons 0%
- Lessons0 / 15
- Completed0
- Est. time left~ 4 hours
Create a free account to keep your progress on every device.
A slow database call should not blank the whole page. Stream a shell immediately, then fill in the slow parts as they finish.
loading.tsx is a boundary
A loading.tsx next to a page.tsx wraps that page in Suspense. Navigation shows the fallback instantly while the page awaits data.
// app/dashboard/loading.tsx
export default function Loading() {
return <p>Loading dashboard…</p>;
}Split the slow bits
If only one widget is slow, do not put the await in the page. Extract it and wrap that component in Suspense so the rest of the page paints first.
// app/dashboard/page.tsx
import { Suspense } from "react";
import { Revenue } from "./revenue";
import { RecentOrders } from "./orders";
export default function Dashboard() {
return (
<>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading revenue…</p>}>
<Revenue />
</Suspense>
<Suspense fallback={<p>Loading orders…</p>}>
<RecentOrders />
</Suspense>
</>
);
}
// revenue.tsx — a server component
export async function Revenue() {
const data = await getRevenue(); // slow
return <p>This month: {data.total}</p>;
}Skeletons beat spinners
Match the fallback layout to the final UI (grey boxes of the same size). The page feels faster because nothing jumps.
Middleware, Cookies and SessionsRun code at the edge for auth redirects and read cookies on the server.