nextjs - ✅(Solved) Fix `router.replace()` restores initial URL query params/hash when replacing with a bare path [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#91658Fetched 2026-04-08 01:02:42
View on GitHub
Comments
2
Participants
2
Timeline
15
Reactions
4
Author
Timeline (top)
subscribed ×7mentioned ×3commented ×2cross-referenced ×1

Root Cause

The bug occurs because router.replace("/") in the App Router appears to preserve (or restore) the query parameters that were present in the initial URL of the session, rather than replacing the full URL with the bare path provided.

Fix Action

Fixed

PR fix notes

PR #91670: fix: use origin for path-absolute URLs in router.replace/push

Description (problem / solution / changelog)

What?

  • Use location.origin for absolute hrefs.
  • Keep location.href for relative hrefs.

Why?

  • Avoid query/hash carry-over.
  • Make URL resolution explicit.

How?

  • Updated app-router-instance.
  • Updated app-router-utils.

Closes : #91658

Changed files

  • packages/next/src/client/components/app-router-instance.ts (modified, +4/-2)
  • packages/next/src/client/components/app-router-utils.ts (modified, +2/-1)

Code Example

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: 1.22.19
  pnpm: 8.15.9
Relevant Packages:
  next: 16.2.1-canary.1 // Latest available version is detected (16.2.1-canary.1).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/dlehmhus/next-router-replace-bug

To Reproduce

  1. Build and start the application
  2. Navigate to the home page with a query parameter, e.g. http://localhost:3000/?query=param
  3. Click "Go to dummy page 1" — this navigates to /dummy-page-1 via <Link>
  4. Click "Go to dummy page 2" — this navigates to /dummy-page-2 via <Link>
  5. Click "Go to home" — this calls router.replace("/")

Current vs. Expected behavior

Following the steps above, I expected router.replace("/") to navigate to / with no query string. Instead, the browser navigates to /?query=param — the query parameters from the very first page load are re-appended to the replaced URL.

The bug occurs because router.replace("/") in the App Router appears to preserve (or restore) the query parameters that were present in the initial URL of the session, rather than replacing the full URL with the bare path provided.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: 1.22.19
  pnpm: 8.15.9
Relevant Packages:
  next: 16.2.1-canary.1 // Latest available version is detected (16.2.1-canary.1).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Linking and Navigating

Which stage(s) are affected? (Select all that apply)

next start (local)

Additional context

This is just a production issue; dev works fine. The issue was introduced in v16.2.0.

extent analysis

Fix Plan

To fix the issue with router.replace("/") preserving query parameters, you can try the following steps:

  • Use the useRouter hook from next/router to get the router object.
  • Call router.replace with an options object that includes shallow: false to ensure a full replacement of the URL.

Example code:

import { useRouter } from 'next/router';

function MyComponent() {
  const router = useRouter();

  const handleGoToHome = () => {
    router.replace("/", { shallow: false });
  };

  return (
    <div>
      <button onClick={handleGoToHome}>Go to home</button>
    </div>
  );
}

Alternatively, you can also try using router.push instead of router.replace:

import { useRouter } from 'next/router';

function MyComponent() {
  const router = useRouter();

  const handleGoToHome = () => {
    router.push("/", { shallow: false });
  };

  return (
    <div>
      <button onClick={handleGoToHome}>Go to home</button>
    </div>
  );
}

Verification

To verify that the fix worked, follow the same steps as before:

  1. Build and start the application
  2. Navigate to the home page with a query parameter, e.g. http://localhost:3000/?query=param
  3. Click "Go to dummy page 1" — this navigates to /dummy-page-1 via <Link>
  4. Click "Go to dummy page 2" — this navigates to /dummy-page-2 via <Link>
  5. Click "Go to home" — this should now navigate to / without any query parameters.

Extra Tips

  • Make sure to test the fix in a production-like environment to ensure it works as expected.
  • If you're using a custom next.config.js file, try removing any custom configurations that may be interfering with the router.replace behavior.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING