fix: scroll to top on forward navigation into nested routes - #56
Conversation
Next.js App Router only scrolls the changed route segment into view, so descending into a nested segment that streams behind a loading.tsx Suspense boundary (e.g. /blog -> /blog/[slug]) left the viewport parked mid-page instead of at the top of the new page. Add a ScrollToTop client component in the locale layout that resets scroll on forward navigation while preserving native back/forward restoration and in-page anchor scrolling.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a client-side ChangesScroll restoration
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Problem
Scrolling down a list page and clicking through to a detail page left the viewport parked partway down the new page — the user landed in the middle of an article instead of at its top.
Root cause
Next.js App Router only scrolls the changed route segment into view. Same-level navigations (e.g.
/→/blog) scroll to the top fine, but descending into a nested segment that streams behind aloading.tsxSuspense boundary (e.g./blog→/blog/[slug],/compare→/compare/[slug]) scrolls that inner segment into view — and re-anchors after the streamed content resolves — leaving the viewport mid-page (measured ~332–666px down).Fix
Add a
ScrollToTopclient component in[locale]/layout.tsxthat resets scroll to the top on forward navigation, while deliberately leaving alone:#hash) navigationsA
requestAnimationFramere-apply guards against Next re-scrolling once the suspended content resolves.Being a client component in the layout, it does not opt any page into dynamic rendering.
Verification (Playwright + project checks)
#faqanchorbun run typecheck✓bun run lint✓ no warningsbun run build✓ all public[locale]pages remain Static/SSG/ISR (●), none turned DynamicSummary by CodeRabbit