-
Notifications
You must be signed in to change notification settings - Fork 27.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add experimental support for history.pushState and history.replaceState #58335
Add experimental support for history.pushState and history.replaceState #58335
Conversation
Current dependencies on/for this PR: This stack of pull requests is managed by Graphite. |
Stats from current PRDefault Build (Increase detected
|
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
buildDuration | 10.6s | 10.6s | N/A |
buildDurationCached | 6s | 6.1s | N/A |
nodeModulesSize | 199 MB | 199 MB | |
nextStartRea..uration (ms) | 418ms | 419ms | N/A |
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
199-HASH.js gzip | 29.1 kB | 29.2 kB | |
3f784ff6-HASH.js gzip | 53.3 kB | 53.3 kB | N/A |
494.HASH.js gzip | 180 B | 181 B | N/A |
framework-HASH.js gzip | 45.2 kB | 45.2 kB | ✓ |
main-app-HASH.js gzip | 241 B | 239 B | N/A |
main-HASH.js gzip | 31.7 kB | 31.8 kB | N/A |
webpack-HASH.js gzip | 1.7 kB | 1.7 kB | ✓ |
Overall change | 76 kB | 76.1 kB |
Legacy Client Bundles (polyfills)
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 31 kB | 31 kB | ✓ |
Overall change | 31 kB | 31 kB | ✓ |
Client Pages
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
_app-HASH.js gzip | 194 B | 195 B | N/A |
_error-HASH.js gzip | 182 B | 181 B | N/A |
amp-HASH.js gzip | 504 B | 506 B | N/A |
css-HASH.js gzip | 322 B | 323 B | N/A |
dynamic-HASH.js gzip | 2.5 kB | 2.5 kB | ✓ |
edge-ssr-HASH.js gzip | 253 B | 255 B | N/A |
head-HASH.js gzip | 348 B | 347 B | N/A |
hooks-HASH.js gzip | 369 B | 368 B | N/A |
image-HASH.js gzip | 4.3 kB | 4.3 kB | N/A |
index-HASH.js gzip | 256 B | 256 B | ✓ |
link-HASH.js gzip | 2.65 kB | 2.65 kB | N/A |
routerDirect..HASH.js gzip | 311 B | 311 B | ✓ |
script-HASH.js gzip | 384 B | 383 B | N/A |
withRouter-HASH.js gzip | 307 B | 308 B | N/A |
1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
Overall change | 3.17 kB | 3.17 kB | ✓ |
Client Build Manifests
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
_buildManifest.js gzip | 486 B | 484 B | N/A |
Overall change | 0 B | 0 B | ✓ |
Rendered Page Sizes
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
index.html gzip | 528 B | 526 B | N/A |
link.html gzip | 540 B | 541 B | N/A |
withRouter.html gzip | 524 B | 521 B | N/A |
Overall change | 0 B | 0 B | ✓ |
Edge SSR bundle Size Overall increase ⚠️
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
edge-ssr.js gzip | 92.5 kB | 92.5 kB | N/A |
page.js gzip | 145 kB | 145 kB | |
Overall change | 145 kB | 145 kB |
Middleware size
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
middleware-b..fest.js gzip | 623 B | 627 B | N/A |
middleware-r..fest.js gzip | 150 B | 151 B | N/A |
middleware.js gzip | 24.8 kB | 24.8 kB | N/A |
edge-runtime..pack.js gzip | 1.92 kB | 1.92 kB | ✓ |
Overall change | 1.92 kB | 1.92 kB | ✓ |
Next Runtimes
vercel/next.js canary | vercel/next.js 11-11-Add_support_for_history.pushState_and_history.replaceState | Change | |
---|---|---|---|
app-page-exp...dev.js gzip | 167 kB | 167 kB | ✓ |
app-page-exp..prod.js gzip | 93.2 kB | 93.2 kB | ✓ |
app-page-tur..prod.js gzip | 93.9 kB | 93.9 kB | ✓ |
app-page-tur..prod.js gzip | 88.5 kB | 88.5 kB | ✓ |
app-page.run...dev.js gzip | 137 kB | 137 kB | ✓ |
app-page.run..prod.js gzip | 87.9 kB | 87.9 kB | ✓ |
app-route-ex...dev.js gzip | 23.8 kB | 23.8 kB | ✓ |
app-route-ex..prod.js gzip | 16.4 kB | 16.4 kB | ✓ |
app-route-tu..prod.js gzip | 16.4 kB | 16.4 kB | ✓ |
app-route-tu..prod.js gzip | 16 kB | 16 kB | ✓ |
app-route.ru...dev.js gzip | 23.2 kB | 23.2 kB | ✓ |
app-route.ru..prod.js gzip | 16 kB | 16 kB | ✓ |
pages-api-tu..prod.js gzip | 9.37 kB | 9.37 kB | ✓ |
pages-api.ru...dev.js gzip | 9.64 kB | 9.64 kB | ✓ |
pages-api.ru..prod.js gzip | 9.37 kB | 9.37 kB | ✓ |
pages-turbo...prod.js gzip | 21.8 kB | 21.8 kB | ✓ |
pages.runtim...dev.js gzip | 22.5 kB | 22.5 kB | ✓ |
pages.runtim..prod.js gzip | 21.8 kB | 21.8 kB | ✓ |
server.runti..prod.js gzip | 48.8 kB | 48.8 kB | ✓ |
Overall change | 922 kB | 922 kB | ✓ |
Diff details
Diff for page.js
Diff too large to display
Diff for edge-ssr.js
Diff too large to display
Diff for 199-HASH.js
Diff too large to display
Tests Passed |
…-Add_support_for_history.pushState_and_history.replaceState
…d_history.replaceState
startTransition(() => { | ||
dispatch({ | ||
type: ACTION_RESTORE, | ||
url: new URL(url ?? window.location.href), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: This should be based on window.location.href
, to allow relative/partial URLs.
One can set search params on the current page (and maintain router state) via history.replaceState(history.state, '', '?foo=bar')
, but this line fails as it's not a valid URL.
suggestion: new URL(url ?? '', window.location.href)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created issue #56636 some time ago. I spotted this experimental feature in the 14.0.3 release notes and thought it could fix it. However, when I try it, I just get TypeError: Failed to construct 'URL': Invalid URL
errors. It looks to be related to this review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const currentState = window.history.state | ||
const __NA = currentState?.__NA | ||
if (__NA) { | ||
data.__NA = __NA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timneutkens I believe data
can be very plausible be null
or undefined
here based on the replaceHistory
API.
Follow-up to #58335. Fixes #48110 (reply in thread) As reported in the discussion passing a origin-relative string didn't work as `new URL` will throw an error. This ensures the `origin` parameter is provided to `new URL`. Added tests for the string behavior. <!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Adding or Updating Examples - The "examples guidelines" are followed from our contributing doc https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md - Make sure the linting passes by running `pnpm build && pnpm lint`. See https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? ### Why? ### How? Closes NEXT- Fixes # --> --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Zack Tanner <[email protected]>
@timneutkens thanks for this feature, it's much needed! But did you forget about useParams hook? |
https://medium.com/@moh.mir36/shallow-routing-with-next-js-v13-app-directory-2d765928c340 I've been using a solution similar to the one mentioned in above article. Unfortunately it no longer works after this pr. What I did was implementing a |
Would it be possible to alter this addition so that it amends whatever the set function for As it stands, these changes break anything else that hooks into the history API such as Plausible analytics. Presumably, other analytics function in similar ways to unobtrusively track page change. |
@justrealmilk I'm guessing the gap between the effect call and when we read window.history.pushState / replaceState in the module is when those override? That can be changed yeah. It would be helpful to know the exact case because then a test can be added. |
Opened a fix here: #58861, this is based on the assumption mentioned above, would be good to get confirmation of the exact case. |
Now, there is an issue with Google Tag Manager "History change" event trigger: |
Similarly looks to cause an issue with Plausible 4lejandrito/next-plausible#107 |
seems like that, usePArams is not using this |
I didn't forget about it, it can't be supported. Client-side you don't know what routes could match. If you want to change parameters you have to use |
So, I have to use first push and then replace ? |
No I meant you can use either, but in that case you just don't shallow route. |
The problem @timneutkens is that in the case of the server request nextjs is losing the state of all tree, is not yet possible to keep the state alive in between path changes ? For example /es/pricing to /en/pricing it should keep the state and just hydrate the changes …., if you just remove the keying you are using it will just hydrate, the keying you are using lose all things shout the needing |
May I ask when it's available in stable NextJs 13.x? |
## What? Fixes #58335 (comment). Waiting on a reply for the exact case but my assumption is that the history is overwritten in the user module instead of before the other JS loads. <!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Adding or Updating Examples - The "examples guidelines" are followed from our contributing doc https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md - Make sure the linting passes by running `pnpm build && pnpm lint`. See https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? ### Why? ### How? Closes NEXT- Fixes # -->
Why wouldnt this be added on the actual router hook? Seems a bit weird that we have to directly use |
What?
This PR introduces support for manually calling
history.pushState
andhistory.replaceState
.It's currently under an experimental flag:
Going forward I'll refer to
history.pushState
asreplaceState
is interchangable.When the flag is enabled you're able to call the web platform
history.pushState
in the usual way:Let's start by explaining what would happen without the flag:
When a new history entry is pushed outside of the Next.js router any back navigation to that history entry will cause a browser reload as it can no longer be used by Next.js as the required metadata for the router is missing. In practice this makes it so that pushState/replaceState is not feasible to be used. Any pathname / searchParams added can't be observed by
usePathname
/useSearchParams
either.With the flag enabled the pushState/replaceState calls are instrumented and is synced into the Next.js router. This way the Next.js router's internal metadata is preserved, making back navigations apply still, and pathname / searchParams is synced as well, making sure that you can observe it using
usePathname
anduseSearchParams
.How?
windowHistorySupport
history.pushState
andhistory.replaceState
Something to figure out is how we handle additional pushes/replaces in Next.js as that should override the history state that was previously set.Went with this after discussing with @sebmarkbage:
window.history.pushState
/window.history.replaceState
and then do an mpa navigation (i.e.<a>
orwindow.location.href
) and the navigate backwards the custom history state is preservedrouter.push()
/<Link>
) the custom history state is not preserved