Suspense below shared layouts
Put the boundary under the layout both routes share.
Next.js docsMessage · blocking-prerender-dynamic
A single boundary in the root layout passes a page-load check but leaves sibling client navigations blocking.
From next-cache-components-optimizer, patterns.md and SKILL.md.
Reload passes, click blocks
Click a tab, then compare with Reload.
Boundary in root layoutBad
app/layout.tsx wraps {children}Boundary in store layoutGood
app/store/layout.tsx wraps {children}// app/store/layout.tsx
import { Suspense } from 'react'
export default function StoreLayout({ children }: { children: React.ReactNode }) {
return (
<section>
<StoreNav />
<Suspense fallback={<Page.Skeleton />}>{children}</Suspense>
</section>
)
}
// or app/store/[slug]/loading.tsx: it covers both loads
Find the divergence point
Pick where the click starts and where it goes.
app/Soft navigation
Why it matters, pros and cons
Why it matters
A click keeps shared layouts mounted and renders only the segments below them. A boundary above that point never shows.
Pros
- Clicks commit at once, with a skeleton.
- The shared nav stays mounted and keeps its state.
- A segment
loading.tsxcovers reloads and clicks.
Cons
- More boundaries, more fallbacks to keep in sync.
- Reload tests won't catch it; test a real
<Link>click. - Per-component boundaries in the page keep even more in the shell.