Skip to content

Commit a4668f2

Browse files
authored
Add handling for back/forward (popstate) between old and new router (#38453)
Handles the case where you navigate between routes in `pages` and `app`. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)
1 parent bb0013f commit a4668f2

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

packages/next/client/components/app-router.client.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,16 @@ export default function AppRouter({
166166
return
167167
}
168168

169+
// Identifier is shortened intentionally.
170+
// __NA is used to identify if the history entry can be handled by the app-router.
171+
// __N is used to identify if the history entry can be handled by the old router.
172+
const historyState = { __NA: true, tree }
169173
if (pushRef.pendingPush) {
170174
pushRef.pendingPush = false
171-
window.history.pushState({ tree }, '', canonicalUrl)
175+
176+
window.history.pushState(historyState, '', canonicalUrl)
172177
} else {
173-
window.history.replaceState({ tree }, '', canonicalUrl)
178+
window.history.replaceState(historyState, '', canonicalUrl)
174179
}
175180
}, [tree, pushRef, canonicalUrl])
176181

@@ -185,6 +190,13 @@ export default function AppRouter({
185190
return
186191
}
187192

193+
// TODO: this case happens when pushState/replaceState was called outside of Next.js or when the history entry was pushed by the old router.
194+
// It reloads the page in this case but we might have to revisit this as the old router ignores it.
195+
if (!state.__NA) {
196+
window.location.reload()
197+
return
198+
}
199+
188200
// @ts-ignore useTransition exists
189201
// TODO: Ideally the back button should not use startTransition as it should apply the updates synchronously
190202
// Without startTransition works if the cache is there for this path
@@ -193,7 +205,7 @@ export default function AppRouter({
193205
type: 'restore',
194206
payload: {
195207
url: new URL(window.location.href),
196-
historyState: state,
208+
tree: state.tree,
197209
},
198210
})
199211
})

packages/next/client/components/reducer.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,6 @@ type AppRouterState = {
247247
canonicalUrl: string
248248
}
249249

250-
type HistoryState = { tree: FlightRouterState }
251-
252250
export function reducer(
253251
state: AppRouterState,
254252
action:
@@ -265,7 +263,7 @@ export function reducer(
265263
}
266264
}
267265
}
268-
| { type: 'restore'; payload: { url: URL; historyState: HistoryState } }
266+
| { type: 'restore'; payload: { url: URL; tree: FlightRouterState } }
269267
| {
270268
type: 'server-patch'
271269
payload: {
@@ -276,14 +274,14 @@ export function reducer(
276274
}
277275
): AppRouterState {
278276
if (action.type === 'restore') {
279-
const { url, historyState } = action.payload
277+
const { url, tree } = action.payload
280278
const href = url.pathname + url.search + url.hash
281279

282280
return {
283281
canonicalUrl: href,
284282
pushRef: state.pushRef,
285283
cache: state.cache,
286-
tree: historyState.tree,
284+
tree: tree,
287285
}
288286
}
289287

packages/next/shared/lib/router/router.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ interface NextHistoryState {
7373

7474
export type HistoryState =
7575
| null
76-
| { __N: false }
77-
| ({ __N: true; key: string } & NextHistoryState)
76+
| { __NA: true; __N?: false }
77+
| { __N: false; __NA?: false }
78+
| ({ __NA?: false; __N: true; key: string } & NextHistoryState)
7879

7980
function buildCancellationError() {
8081
return Object.assign(new Error('Route Cancelled'), {
@@ -840,6 +841,12 @@ export default class Router implements BaseRouter {
840841
return
841842
}
842843

844+
// __NA is used to identify if the history entry can be handled by the app-router.
845+
if (state.__NA) {
846+
window.location.reload()
847+
return
848+
}
849+
843850
if (!state.__N) {
844851
return
845852
}

0 commit comments

Comments
 (0)