From 9ffde72ced0437b24ba3e877e314c6c138607c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Berg=C3=A9?= Date: Tue, 10 Mar 2026 18:18:40 +0100 Subject: [PATCH] Reset scroll between section navigation (#4089) --- .../src/components/hooks/useScrollPage.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/gitbook/src/components/hooks/useScrollPage.ts b/packages/gitbook/src/components/hooks/useScrollPage.ts index 34ff59a54..a2751b317 100644 --- a/packages/gitbook/src/components/hooks/useScrollPage.ts +++ b/packages/gitbook/src/components/hooks/useScrollPage.ts @@ -6,6 +6,25 @@ import { usePathname } from 'next/navigation'; import { useHash } from './useHash'; import { usePrevious } from './usePrevious'; +const SCROLL_PAGE_INITIAL_LOAD_KEY = '__gitbookScrollPageInitialLoadHandled'; + +type ScrollPageWindow = Window & { + [SCROLL_PAGE_INITIAL_LOAD_KEY]?: boolean; +}; + +/** + * Check if it's an initial page load outside of React tree. + */ +function isInitialPageLoad() { + const scrollPageWindow = window as ScrollPageWindow; + if (scrollPageWindow[SCROLL_PAGE_INITIAL_LOAD_KEY]) { + return false; + } + + scrollPageWindow[SCROLL_PAGE_INITIAL_LOAD_KEY] = true; + return true; +} + /** * Handles scroll behavior when the URL hash changes during client-side navigation. */ @@ -18,7 +37,13 @@ function useScrollPage() { // Never scroll on initial rendering to avoid blocking: // • Native browser hash scrolling // • Scroll-to-text fragments (undetectable) - if (previous === undefined || (previous.hash === hash && previous.pathname === pathname)) { + if (previous === undefined) { + // If previous is undefined, we rely on a check outside React tree, + // sections are remounting everything so we can't rely on React. + if (isInitialPageLoad()) { + return; + } + } else if (previous.hash === hash && previous.pathname === pathname) { return; }