Skip to content

Commit c381550

Browse files
authored
fix(landing-nav): scroll to top on route change in shared shells (#4676)
* fix(landing-nav): scroll to top on route change in shared shells * fix(landing-nav): hoist popstate flag to module scope and skip on hash anchors * fix(landing-nav): use popstate timestamp window so flag self-expires * fix(landing-nav): use -Infinity sentinel and consume popstate timestamp on use * fix(landing-nav): skip scroll on initial mount so reload restoration wins * fix(landing-nav): gate initial-mount skip on document.readyState * refactor(landing-nav): simplify scroll-to-top to conventional pattern
1 parent af8025c commit c381550

4 files changed

Lines changed: 28 additions & 0 deletions

File tree

apps/sim/app/(landing)/blog/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getNavBlogPosts } from '@/lib/blog/registry'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33
import Footer from '@/app/(landing)/components/footer/footer'
44
import Navbar from '@/app/(landing)/components/navbar/navbar'
5+
import { ScrollToTop } from '@/app/(landing)/components/scroll-to-top'
56

67
export default async function StudioLayout({ children }: { children: React.ReactNode }) {
78
const blogPosts = await getNavBlogPosts()
@@ -29,6 +30,7 @@ export default async function StudioLayout({ children }: { children: React.React
2930

3031
return (
3132
<div className='flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
33+
<ScrollToTop />
3234
<script
3335
type='application/ld+json'
3436
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
import { usePathname } from 'next/navigation'
5+
6+
/**
7+
* Resets window scroll to the top on App Router pathname changes.
8+
*
9+
* Next.js's default scroll handling only brings the new Page element into view,
10+
* which often resolves to "no scroll" inside shared layouts (see vercel/next.js#64435).
11+
* Skipped when a hash anchor is targeted so the browser's native anchor scroll wins.
12+
*/
13+
export function ScrollToTop() {
14+
const pathname = usePathname()
15+
16+
useEffect(() => {
17+
if (window.location.hash) return
18+
window.scrollTo(0, 0)
19+
}, [pathname])
20+
21+
return null
22+
}

apps/sim/app/(landing)/integrations/(shell)/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getNavBlogPosts } from '@/lib/blog/registry'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33
import Footer from '@/app/(landing)/components/footer/footer'
44
import Navbar from '@/app/(landing)/components/navbar/navbar'
5+
import { ScrollToTop } from '@/app/(landing)/components/scroll-to-top'
56

67
export default async function IntegrationsLayout({ children }: { children: React.ReactNode }) {
78
const blogPosts = await getNavBlogPosts()
@@ -29,6 +30,7 @@ export default async function IntegrationsLayout({ children }: { children: React
2930

3031
return (
3132
<div className='dark flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
33+
<ScrollToTop />
3234
<script
3335
type='application/ld+json'
3436
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}

apps/sim/app/(landing)/models/(shell)/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getNavBlogPosts } from '@/lib/blog/registry'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33
import Footer from '@/app/(landing)/components/footer/footer'
44
import Navbar from '@/app/(landing)/components/navbar/navbar'
5+
import { ScrollToTop } from '@/app/(landing)/components/scroll-to-top'
56

67
export default async function ModelsLayout({ children }: { children: React.ReactNode }) {
78
const blogPosts = await getNavBlogPosts()
@@ -24,6 +25,7 @@ export default async function ModelsLayout({ children }: { children: React.React
2425

2526
return (
2627
<div className='dark flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
28+
<ScrollToTop />
2729
<script
2830
type='application/ld+json'
2931
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}

0 commit comments

Comments
 (0)