Skip to content

Commit 42c3f3b

Browse files
fix: scroll restoration upon browser forward navigation (#7055)
1 parent 5c5a435 commit 42c3f3b

3 files changed

Lines changed: 66 additions & 18 deletions

File tree

.changeset/fifty-eagles-breathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/router-core': patch
3+
---
4+
5+
Fix a regression where browser back/forward navigation could fail to restore the previous scroll position for an existing history entry.

e2e/react-router/scroll-restoration-sandbox-vite/tests/app.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,67 @@ test('Smoke - Renders home', async ({ page }) => {
99
).toBeVisible()
1010
})
1111

12+
test('restores the prior scroll position after browser back then forward', async ({
13+
page,
14+
}) => {
15+
await page.goto(toRuntimePath('/'))
16+
await page.getByRole('link', { name: 'Head-/normal-page' }).click()
17+
await page.waitForURL('**/normal-page')
18+
await expect(page.getByTestId('at-the-top')).toBeInViewport()
19+
20+
const scrollY = await page.evaluate(async () => {
21+
window.scrollTo(0, document.documentElement.scrollHeight)
22+
await new Promise((resolve) => requestAnimationFrame(() => resolve(null)))
23+
return window.scrollY
24+
})
25+
26+
expect(scrollY).toBeGreaterThan(0)
27+
await expect(page.getByTestId('at-the-bottom')).toBeInViewport()
28+
29+
await page.goBack()
30+
await expect(
31+
page.getByRole('heading', { name: 'Welcome Home!' }),
32+
).toBeVisible()
33+
34+
await page.goForward()
35+
await page.waitForURL('**/normal-page')
36+
await page.waitForFunction(
37+
(expectedScrollY) => Math.abs(window.scrollY - expectedScrollY) <= 2,
38+
scrollY,
39+
)
40+
await expect(page.getByTestId('at-the-bottom')).toBeInViewport()
41+
})
42+
43+
test('restores the prior scroll position after browser back navigation', async ({
44+
page,
45+
}) => {
46+
await page.goto(toRuntimePath('/normal-page'))
47+
await expect(page.getByTestId('at-the-top')).toBeInViewport()
48+
49+
const scrollY = await page.evaluate(async () => {
50+
window.scrollTo(0, document.documentElement.scrollHeight)
51+
await new Promise((resolve) => requestAnimationFrame(() => resolve(null)))
52+
return window.scrollY
53+
})
54+
55+
expect(scrollY).toBeGreaterThan(0)
56+
await expect(page.getByTestId('at-the-bottom')).toBeInViewport()
57+
58+
await page.getByRole('link', { name: 'Foot-/', exact: true }).click()
59+
await page.waitForURL('**/')
60+
await expect(
61+
page.getByRole('heading', { name: 'Welcome Home!' }),
62+
).toBeVisible()
63+
64+
await page.goBack()
65+
await page.waitForURL('**/normal-page')
66+
await page.waitForFunction(
67+
(expectedScrollY) => Math.abs(window.scrollY - expectedScrollY) <= 2,
68+
scrollY,
69+
)
70+
await expect(page.getByTestId('at-the-bottom')).toBeInViewport()
71+
})
72+
1273
const pages = [
1374
linkOptions({ to: '/normal-page' }),
1475
linkOptions({ to: '/lazy-page' }),

packages/router-core/src/scroll-restoration.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -258,24 +258,6 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
258258
return
259259
}
260260

261-
const fromIndex = event.fromLocation?.state.__TSR_index
262-
const toIndex = event.toLocation.state.__TSR_index
263-
// Clear on forward navigations, and on same-entry replace navigations where
264-
// the href changed. Preserve back/restore entries so they can be restored.
265-
const shouldClearCache =
266-
typeof fromIndex === 'number' && typeof toIndex === 'number'
267-
? toIndex > fromIndex ||
268-
(toIndex === fromIndex &&
269-
event.fromLocation?.href !== event.toLocation.href)
270-
: true
271-
272-
if (shouldClearCache) {
273-
cache.set((state) => {
274-
delete state[cacheKey]
275-
return state
276-
})
277-
}
278-
279261
ignoreScroll = true
280262

281263
try {

0 commit comments

Comments
 (0)