Fix anchor links not scrolling to target on client-side navigation (RND-11844)

Cross-page anchor links (e.g. a homepage card linking to `/page#heading`)
landed at the top of the destination page on soft navigation, while the same
URL loaded directly scrolled to the heading correctly.

`useScrollToHash` (the per-page safety net in PageClientLayout that scrolls
once the page blocks are rendered) only depended on the navigation hash. The
hash is set at click time while the previous page is still mounted, so the
effect fired before the target element existed. Because sibling pages share the
same `[pagePath]` route, PageClientLayout is reused rather than remounted, so
the effect never re-ran once the destination content committed to the DOM.

Add `pathname` as a dependency so the scroll is re-attempted when the
destination page commits and the target heading is present. The `if (hash)`
guard is unchanged, so hash-less navigations still scroll to top via ScrollPage
and back/forward restoration is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TpGe6QkAF4Y1HDGUD4vbke
This commit is contained in:
Claude
2026-07-14 00:17:19 +00:00
parent a69a307de2
commit 0146877fc1
2 changed files with 14 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Fix anchor links (e.g. `/page#heading`) not scrolling to the target heading during client-side navigation between pages.
@@ -69,12 +69,20 @@ export function ScrollPage() {
*/
export function useScrollToHash() {
const hash = useHash();
const pathname = usePathname();
// Depend on `pathname` as well as `hash`: on a soft navigation to another page
// (e.g. a homepage card linking to `/page#heading`), the hash is set on click while
// the previous page is still mounted, so a hash-only effect fires before the target
// element exists. Because sibling pages share the same `[pagePath]` route, this hook's
// host component is reused rather than remounted, so it would otherwise never re-run
// once the destination content commits. Re-running on `pathname` re-attempts the scroll
// when the target heading is finally in the DOM.
React.useEffect(() => {
if (hash) {
scrollToHash(hash);
}
}, [hash]);
}, [hash, pathname]);
}
/**