Suspense and Streaming
Suspense specifies loading UI while async content loads, enabling streaming in Next.js.
Example: Dashboard
From app/dashboard/page.tsx:
export default function DashboardPage({ searchParams }) {
return (
<div>
<Suspense fallback={<PostTabsSkeleton />}>
<PostTabs />
</Suspense>
<Suspense fallback={<PostListSkeleton />}>
<PostList searchParams={searchParams} />
</Suspense>
</div>
);
}Separate boundaries let each section stream independently.
Co-locating Skeletons
From app/dashboard/_components/PostList.tsx:
export async function PostList({ searchParams }) {
const posts = await getPosts(filter);
return posts.map(post => <Card key={post.slug}>...</Card>);
}
export function PostListSkeleton() {
return (
<div className="space-y-4">
{[1, 2, 3].map(i => (
<Card key={i}>
<Skeleton className="h-6 w-48" />
<Skeleton className="h-4 w-24" />
</Card>
))}
</div>
);
}Export skeletons alongside their components to keep them in sync.