Skip to content

Commit 2ecfa51

Browse files
committed
fix(landing-nav): use popstate timestamp window so flag self-expires
1 parent 35b29e6 commit 2ecfa51

1 file changed

Lines changed: 15 additions & 13 deletions

File tree

apps/sim/app/(landing)/components/scroll-to-top.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,21 @@ import { useEffect } from 'react'
44
import { usePathname } from 'next/navigation'
55

66
/**
7-
* Module-level flag so the popstate signal survives layout remounts when the
8-
* user navigates across sections (e.g., blog ↔ integrations ↔ models), where
9-
* the outgoing layout — and its component instances — unmount before the
10-
* incoming layout's effect runs.
7+
* Timestamp of the most recent popstate. Module-scoped so it survives layout
8+
* remounts when navigating across sections (e.g., blog ↔ integrations ↔ models),
9+
* where the outgoing layout's component instances unmount before the incoming
10+
* layout's effect runs.
11+
*
12+
* A timestamp window (rather than a sticky boolean) self-expires the signal —
13+
* a hash-only back/forward fires popstate but doesn't change `usePathname`, so
14+
* a boolean would never be consumed and would poison the next real navigation.
1115
*/
12-
let isPopNavigation = false
16+
let lastPopstateAt = 0
17+
const POPSTATE_WINDOW_MS = 200
1318

1419
if (typeof window !== 'undefined') {
1520
window.addEventListener('popstate', () => {
16-
isPopNavigation = true
21+
lastPopstateAt = performance.now()
1722
})
1823
}
1924

@@ -23,18 +28,15 @@ if (typeof window !== 'undefined') {
2328
* Next.js's default scroll handling only brings the new Page element into view,
2429
* which often resolves to "no scroll" inside shared layouts (see vercel/next.js#64435).
2530
*
26-
* Popstate-driven navigations are skipped so browser back/forward scroll
27-
* restoration is preserved, and hash-anchor navigations are skipped so the
28-
* browser's native anchor scroll wins.
31+
* Skipped when the pathname change closely follows a popstate (preserving browser
32+
* back/forward scroll restoration) or when a hash anchor is targeted (letting the
33+
* browser's native anchor scroll win).
2934
*/
3035
export function ScrollToTop() {
3136
const pathname = usePathname()
3237

3338
useEffect(() => {
34-
if (isPopNavigation) {
35-
isPopNavigation = false
36-
return
37-
}
39+
if (performance.now() - lastPopstateAt < POPSTATE_WINDOW_MS) return
3840
if (window.location.hash) return
3941
window.scrollTo(0, 0)
4042
}, [pathname])

0 commit comments

Comments
 (0)