Await in the leaf
Pass request promises down; await them inside a Suspense child.
Next.js docsMessage · blocking-prerender-runtime
Awaiting request-time data at the top of a page/layout makes everything below it dynamic.
From next-cache-components-optimizer, patterns.md and real-app-patterns.md.
One await, whole page waits
Pick what the route reads, then press Request.
Await at the topBad
Await in the leafGood
Defer the auth gate
Pick a visitor, then press Visit.
Gate at the topBad
await getSession() then redirect()Gate in SuspenseGood
<Suspense fallback={null}><AuthGate />// app/settings/layout.tsx
import { Suspense } from 'react'
export default function SettingsLayout({ children }) {
return (
<Shell>
<Suspense fallback={null}>
<AuthGate />
</Suspense>
{children}
</Shell>
)
}
async function AuthGate() {
const session = await getSession()
if (!session?.user) redirect(getLoginUrl())
return null
}
Why it matters, pros and cons
Why it matters
A top-level await suspends the prerender, so nothing below it reaches the static shell.
Pros
- Nav, heading and layout paint from the shell.
- Only the part that reads request data waits.
redirect()still runs, at request time.
Cons
- Each reader needs its own boundary and fallback.
- A deferred gate lets the frame paint; keep user data out of it.
- A page that is all URL data has little shell to gain.