# Refactor patterns — push dynamic down into the shell Each pattern is **before → after**: keep as much as possible in the prerendered shell, and wrap only genuinely per-request work in a tight `` (or hoist it into `use cache`). Production shapes — parallel-route slots, deferring an auth gate, client slot-routers — are in `real-app-patterns.md`. --- ## 1. Awaiting at the top → move the await into a Suspense child The most common blocking shape. Awaiting request-time data at the top of a page/layout makes **everything below it** dynamic. ```tsx // ❌ before — top-level await of a non-static param + uncached data export default async function Page(props: PageProps<'/store/[slug]'>) { const { slug } = await props.params const product = await db.products.findBySlug(slug) return (

{product.name}

) } ``` ```tsx // ✅ after — pass the params promise down; await inside a Suspense-wrapped child import { Suspense } from 'react' export default function Page(props: PageProps<'/store/[slug]'>) { return ( Loading product…

}>
) } async function Product({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params const product = await db.products.findBySlug(slug) return (

{product.name}

) } ``` Inline variant when you don't want a separate component — unwrap the promise without awaiting at the top: ```tsx export default function Page(props: PageProps<'/store/[category]'>) { return ( }> {props.params.then(({ category }) => ( ))} ) } ``` **Insight:** [runtime data during prerendering](https://nextjs.org/docs/messages/blocking-prerender-runtime). --- ## 2. `cookies()` / `headers()` in a layout → start, don't await; pass down A layout that awaits request data blocks the layout **and every page under it**. ```tsx // ❌ before — whole layout (and all children) becomes dynamic export default async function Layout({ children }) { const cookieStore = await cookies() const theme = cookieStore.get('theme')?.value return {children} } ``` ```tsx // ✅ after — start the read without awaiting, pass the promise to a Suspense child import { Suspense } from 'react' import { cookies } from 'next/headers' export default function Layout({ children }: { children: React.ReactNode }) { const cookieStore = cookies() // not awaited → does not block the shell return ( {children} ) } async function UserMenu({ cookiePromise, }: { cookiePromise: ReturnType }) { const theme = (await cookiePromise).get('theme')?.value return
} ``` `{children}` and `