nextjs - ✅(Solved) Fix `useSearchParams() instanceof ReadonlyURLSearchParams` returns `false` on the server and `true` on the client. [2 pull requests, 1 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#86501Fetched 2026-04-08 02:10:38
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
0
Timeline (top)
cross-referenced ×2closed ×1commented ×1issue_type_added ×1

Fix Action

Fixed

PR fix notes

PR #86649: Fix Suspense DevTools instrumentation in all navigation hooks during SSR in dev

Description (problem / solution / changelog)

What?

This PR extends the SSR dev-mode fix for Suspense DevTools instrumentation beyond useSearchParams() to all other navigation hooks that use the same instrumentation pattern:

Each of these hooks currently applies React Suspense DevTools instrumentation in development mode, including during server-side rendering, which causes incorrect behavior.

Why?

During SSR in development mode, React’s Suspense DevTools instrumentation wraps values via:

use(NavigationPromisesContext)

This causes the resolved values to originate from a different module context. As a result:

  • identity-based checks (e.g. instanceof) fail,
  • type expectations break,
  • return values differ between dev SSR and production SSR,
  • and navigation hooks behave inconsistently.

This issue was fixed for useSearchParams(), but the same pattern appears in other navigation hooks, meaning the bug still occurs elsewhere.

To ensure consistent behavior across the entire navigation API, all hooks using Suspense DevTools instrumentation need the same fix.

How?

The fix adds the same guard used in the existing useSearchParams() patch:

typeof window !== 'undefined'

so that Suspense DevTools instrumentation is only applied on the client.

The updated condition now ensures:

  • dev mode
  • React supports use
  • and we are running in the browser
if (
  process.env.NODE_ENV !== 'production' &&
  'use' in React &&
  typeof window !== 'undefined'
) {

This prevents instrumentation from running during SSR in dev mode, eliminating module-context mismatches while preserving full client-side DevTools support.

Fixes #86501

Changed files

  • packages/next/src/client/components/navigation.ts (modified, +22/-5)

PR #87269: Ensure constructor for useSearchParams can be imported for instanceof checks

Description (problem / solution / changelog)

Fixes https://github.com/vercel/next.js/issues/86501 Closes https://github.com/vercel/next.js/pull/86649

The same constructor is relevant for instanceof checks. Previously useSearchParams() instanceof ReadonlyURLSearchParams lead to hydration mismatches.

Changed files

  • packages/next/src/client/components/navigation-devtools.ts (modified, +2/-2)
  • packages/next/src/client/components/navigation.ts (modified, +9/-5)
  • packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts (modified, +5/-2)
  • test/e2e/app-dir/hooks/app/hooks/use-search-params/instanceof/page.js (added, +42/-0)
  • test/e2e/app-dir/hooks/hooks.test.ts (modified, +20/-0)

Code Example

CodeSandbox environment:

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Sun Aug  6 20:05:33 UTC 2023
  Available memory (MB): 4102
  Available CPU cores: 2
Binaries:
  Node: 20.12.1
  npm: 10.5.0
  Yarn: 1.22.19
  pnpm: 8.15.6
Relevant Packages:
  next: 16.1.0-canary.2 // Latest available version is detected (16.1.0-canary.2).
  eslint-config-next: N/A
  react: 19.2.0
  react-dom: 19.2.0
  typescript: 5.9.3
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://codesandbox.io/p/devbox/adoring-nobel-36s846?file=%2Fapp%2Fclient.tsx

To Reproduce

Refresh the CodeSandbox preview and see the page content change from false to true.

Current vs. Expected behavior

According to type definitions, useSearchParams() returns a ReadonlyURLSearchParams instance. While rendering a client component on the server, useSearchParams() instanceof ReadonlyURLSearchParams returns false, but when hydration starts and the client component is rendered on the client, useSearchParams() instanceof ReadonlyURLSearchParams returns true.

This happens only while using the dev server, with the prod server, useSearchParams() instanceof ReadonlyURLSearchParams always returns true.

I expect useSearchParams() instanceof ReadonlyURLSearchParams to always return true when using the dev server.

Provide environment information

CodeSandbox environment:

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Sun Aug  6 20:05:33 UTC 2023
  Available memory (MB): 4102
  Available CPU cores: 2
Binaries:
  Node: 20.12.1
  npm: 10.5.0
  Yarn: 1.22.19
  pnpm: 8.15.6
Relevant Packages:
  next: 16.1.0-canary.2 // Latest available version is detected (16.1.0-canary.2).
  eslint-config-next: N/A
  react: 19.2.0
  react-dom: 19.2.0
  typescript: 5.9.3
Next.js Config:
  output: N/A

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

Not sure

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

next dev (local)

Additional context

I tested my reproduction against different canary releases, and the first one that introduced the bug was "16.0.0-canary.16", since reverting to "16.0.0-canary.15" works.

For context, I'm trying to write a SearchParams wrapper class that can take both the result of useSearchParams() or the searchParams object passed to page components in its constructor, and do different things based on what was passed.

extent analysis

TL;DR

The issue can be mitigated by checking the type of useSearchParams() return value in a way that works for both server and client rendering.

Guidance

  • Verify that the issue is specific to the dev server by testing with a prod server, as the problem does not occur in production.
  • Check the type of useSearchParams() return value using a method that works for both server and client rendering, such as using the instanceof operator with a check for URLSearchParams or ReadonlyURLSearchParams.
  • Consider using a type guard or a wrapper function to handle the difference in type between server and client rendering.
  • Test the solution with different canary releases to ensure the fix works across versions.

Example

function isReadonlyURLSearchParams(params: any): params is ReadonlyURLSearchParams {
  return params instanceof URLSearchParams || params instanceof ReadonlyURLSearchParams;
}

Notes

The issue seems to be related to the difference in behavior between server and client rendering in Next.js. The provided example code snippet is a possible solution, but it may need to be adapted to the specific use case.

Recommendation

Apply a workaround, such as using a type guard or a wrapper function, to handle the difference in type between server and client rendering. This is because the issue is specific to the dev server and does not occur in production, and upgrading to a fixed version may not be possible or necessary.

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