diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index 3d0cf8b7ee230d..7a13c2735f0e1f 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -114,6 +114,8 @@ function navigateReducer_noPPR( const mutable: Mutable = {} const { hash } = url const href = createHrefFromUrl(url) + const hrefNoHash = createHrefFromUrl(url, false) + const isHashOnlyChange = hrefNoHash === state.nextUrl && !!hash const pendingPush = navigateType === 'push' // we want to prune the prefetch cache on every navigation to avoid it growing too large prunePrefetchCache(state.prefetchCache) @@ -201,12 +203,15 @@ function navigateReducer_noPPR( if ( prefetchValues.status === PrefetchCacheEntryStatus.stale && + !isHashOnlyChange && !isFirstRead ) { // When we have a stale prefetch entry, we only want to re-use the loading state of the route we're navigating to, to support instant loading navigations // this will trigger a lazy fetch for the actual page data by nulling the `rsc` and `prefetchRsc` values for page data, // while copying over the `loading` for the segment that contains the page data. // We only do this on subsequent reads, as otherwise there'd be no loading data to re-use. + + // We skip this branch if only the hash fragment has changed, as we don't want to trigger a lazy fetch in that case applied = triggerLazyFetchForLeafSegments( cache, currentCache, @@ -288,7 +293,9 @@ function navigateReducer_PPR( const mutable: Mutable = {} const { hash } = url const href = createHrefFromUrl(url) + const hrefNoHash = createHrefFromUrl(url, false) const pendingPush = navigateType === 'push' + const isHashOnlyChange = hrefNoHash === state.nextUrl && !!hash // we want to prune the prefetch cache on every navigation to avoid it growing too large prunePrefetchCache(state.prefetchCache) @@ -452,12 +459,15 @@ function navigateReducer_PPR( if ( prefetchValues.status === PrefetchCacheEntryStatus.stale && + !isHashOnlyChange && !isFirstRead ) { // When we have a stale prefetch entry, we only want to re-use the loading state of the route we're navigating to, to support instant loading navigations // this will trigger a lazy fetch for the actual page data by nulling the `rsc` and `prefetchRsc` values for page data, // while copying over the `loading` for the segment that contains the page data. // We only do this on subsequent reads, as otherwise there'd be no loading data to re-use. + + // We skip this branch if only the hash fragment has changed, as we don't want to trigger a lazy fetch in that case applied = triggerLazyFetchForLeafSegments( cache, currentCache, diff --git a/test/e2e/app-dir/navigation/app/hash/page.js b/test/e2e/app-dir/navigation/app/hash/page.js index a1c0ab4a13f318..d88a34f3da3971 100644 --- a/test/e2e/app-dir/navigation/app/hash/page.js +++ b/test/e2e/app-dir/navigation/app/hash/page.js @@ -29,6 +29,11 @@ export default function HashPage() { To non-existent +