← Back
Edit Post
Title
Description
cacheComponents default, what triggers dynamic rendering, push dynamic access deep.
Content
Markdown supported
# Static vs Dynamic Rendering Static pages have no in-between state—content is ready when the user arrives. Dynamic pages need Suspense boundaries to show progress while data loads. Understanding what triggers each mode is essential for building fast, responsive UIs. With `cacheComponents: true` in Next.js 16, **rendering is dynamic by default**. You opt into static/cached rendering explicitly with `"use cache"`. ## What Makes a Route Dynamic? Any of these trigger dynamic rendering: - Reading `searchParams` or `params` - Calling `cookies()` or `headers()` - Data fetches without `"use cache"` directive - Using `connection()` to opt into dynamic rendering ## Static Example: Cached Queries When your data fetching function uses `"use cache"`, the content can be pre-rendered: ```tsx // data/queries/post.ts export const getPublishedPosts = cache(async () => { 'use cache'; cacheTag('posts'); return await prisma.post.findMany({ where: { published: true } }); }); // app/page.tsx - No Suspense needed for cached content export default async function HomePage() { const posts = await getPublishedPosts(); return <PostList posts={posts} />; } ``` The posts are cached—no loading state needed because data is ready. ## Dynamic Example: URL Parameters When your component reads `searchParams`, it becomes dynamic and needs Suspense: ```tsx export default function DashboardPage({ searchParams }) { return ( <Suspense fallback={<PostListSkeleton />}> <PostList searchParams={searchParams} /> </Suspense> ); } ``` Inside `PostList`, the filter and sort values come from the URL—content can't be pre-rendered. ## Push Dynamic Access Deep Maximize cacheable content by pushing dynamic data access as deep as possible: ```tsx // ❌ Entire page becomes dynamic export default async function Page({ searchParams }) { const { filter } = await searchParams; const posts = await getPosts(filter); return <Layout><Posts posts={posts} /></Layout>; } // ✅ Only PostList is dynamic; Layout stays static export default function Page({ searchParams }) { return ( <Layout> <Suspense fallback={<PostListSkeleton />}> <PostList searchParams={searchParams} /> </Suspense> </Layout> ); } ``` The Layout renders immediately. PostList streams in with its skeleton. ## Hybrid Pages Most real pages mix static and dynamic content: ```tsx export default function DashboardPage({ searchParams }) { return ( <> {/* Static - renders immediately */} <DashboardHeader /> {/* Dynamic - needs searchParams */} <Suspense fallback={<PostListSkeleton />}> <PostList searchParams={searchParams} /> </Suspense> </> ); } ``` Users see the header instantly while posts stream in.
Published
Save Changes
Cancel