diff --git a/packages/e2e/next/cypress/e2e/shared/conditional-rendering.cy.ts b/packages/e2e/next/cypress/e2e/shared/conditional-rendering.cy.ts
new file mode 100644
index 000000000..07952ea1f
--- /dev/null
+++ b/packages/e2e/next/cypress/e2e/shared/conditional-rendering.cy.ts
@@ -0,0 +1,25 @@
+import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
+
+testConditionalRendering({
+ path: '/app/conditional-rendering/useQueryState',
+ hook: 'useQueryState',
+ nextJsRouter: 'app'
+})
+
+testConditionalRendering({
+ path: '/app/conditional-rendering/useQueryStates',
+ hook: 'useQueryStates',
+ nextJsRouter: 'app'
+})
+
+testConditionalRendering({
+ path: '/pages/conditional-rendering/useQueryState',
+ hook: 'useQueryState',
+ nextJsRouter: 'pages'
+})
+
+testConditionalRendering({
+ path: '/pages/conditional-rendering/useQueryStates',
+ hook: 'useQueryStates',
+ nextJsRouter: 'pages'
+})
diff --git a/packages/e2e/next/src/app/app/(shared)/conditional-rendering/useQueryState/page.tsx b/packages/e2e/next/src/app/app/(shared)/conditional-rendering/useQueryState/page.tsx
new file mode 100644
index 000000000..ef9ff2ed0
--- /dev/null
+++ b/packages/e2e/next/src/app/app/(shared)/conditional-rendering/useQueryState/page.tsx
@@ -0,0 +1,10 @@
+import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
+import { Suspense } from 'react'
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
diff --git a/packages/e2e/next/src/app/app/(shared)/conditional-rendering/useQueryStates/page.tsx b/packages/e2e/next/src/app/app/(shared)/conditional-rendering/useQueryStates/page.tsx
new file mode 100644
index 000000000..923430312
--- /dev/null
+++ b/packages/e2e/next/src/app/app/(shared)/conditional-rendering/useQueryStates/page.tsx
@@ -0,0 +1,10 @@
+import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
+import { Suspense } from 'react'
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
diff --git a/packages/e2e/next/src/pages/pages/conditional-rendering/useQueryState.tsx b/packages/e2e/next/src/pages/pages/conditional-rendering/useQueryState.tsx
new file mode 100644
index 000000000..42089b432
--- /dev/null
+++ b/packages/e2e/next/src/pages/pages/conditional-rendering/useQueryState.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryState
diff --git a/packages/e2e/next/src/pages/pages/conditional-rendering/useQueryStates.tsx b/packages/e2e/next/src/pages/pages/conditional-rendering/useQueryStates.tsx
new file mode 100644
index 000000000..8715144e1
--- /dev/null
+++ b/packages/e2e/next/src/pages/pages/conditional-rendering/useQueryStates.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryStates
diff --git a/packages/e2e/react-router/v6/cypress/e2e/shared/conditional-rendering.cy.ts b/packages/e2e/react-router/v6/cypress/e2e/shared/conditional-rendering.cy.ts
new file mode 100644
index 000000000..333517391
--- /dev/null
+++ b/packages/e2e/react-router/v6/cypress/e2e/shared/conditional-rendering.cy.ts
@@ -0,0 +1,11 @@
+import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryState',
+ hook: 'useQueryState'
+})
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryStates',
+ hook: 'useQueryStates'
+})
diff --git a/packages/e2e/react-router/v6/src/react-router.tsx b/packages/e2e/react-router/v6/src/react-router.tsx
index 702acce03..5cffb4d09 100644
--- a/packages/e2e/react-router/v6/src/react-router.tsx
+++ b/packages/e2e/react-router/v6/src/react-router.tsx
@@ -41,6 +41,8 @@ const router = createBrowserRouter(
+
+
diff --git a/packages/e2e/react-router/v6/src/routes/conditional-rendering.useQueryState.tsx b/packages/e2e/react-router/v6/src/routes/conditional-rendering.useQueryState.tsx
new file mode 100644
index 000000000..42089b432
--- /dev/null
+++ b/packages/e2e/react-router/v6/src/routes/conditional-rendering.useQueryState.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryState
diff --git a/packages/e2e/react-router/v6/src/routes/conditional-rendering.useQueryStates.tsx b/packages/e2e/react-router/v6/src/routes/conditional-rendering.useQueryStates.tsx
new file mode 100644
index 000000000..8715144e1
--- /dev/null
+++ b/packages/e2e/react-router/v6/src/routes/conditional-rendering.useQueryStates.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryStates
diff --git a/packages/e2e/react-router/v7/app/routes.ts b/packages/e2e/react-router/v7/app/routes.ts
index d55ccfbc0..29751d350 100644
--- a/packages/e2e/react-router/v7/app/routes.ts
+++ b/packages/e2e/react-router/v7/app/routes.ts
@@ -24,6 +24,8 @@ export default [
route('/form/useQueryStates', './routes/form.useQueryStates.tsx'),
route('/referential-stability/useQueryState', './routes/referential-stability.useQueryState.tsx'),
route('/referential-stability/useQueryStates', './routes/referential-stability.useQueryStates.tsx'),
+ route('/conditional-rendering/useQueryState', './routes/conditional-rendering.useQueryState.tsx'),
+ route('/conditional-rendering/useQueryStates', './routes/conditional-rendering.useQueryStates.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/no-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.no-loader.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/sync-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/async-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.async-loader.tsx'),
diff --git a/packages/e2e/react-router/v7/app/routes/conditional-rendering.useQueryState.tsx b/packages/e2e/react-router/v7/app/routes/conditional-rendering.useQueryState.tsx
new file mode 100644
index 000000000..42089b432
--- /dev/null
+++ b/packages/e2e/react-router/v7/app/routes/conditional-rendering.useQueryState.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryState
diff --git a/packages/e2e/react-router/v7/app/routes/conditional-rendering.useQueryStates.tsx b/packages/e2e/react-router/v7/app/routes/conditional-rendering.useQueryStates.tsx
new file mode 100644
index 000000000..8715144e1
--- /dev/null
+++ b/packages/e2e/react-router/v7/app/routes/conditional-rendering.useQueryStates.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryStates
diff --git a/packages/e2e/react-router/v7/cypress/e2e/shared/conditional-rendering.cy.ts b/packages/e2e/react-router/v7/cypress/e2e/shared/conditional-rendering.cy.ts
new file mode 100644
index 000000000..333517391
--- /dev/null
+++ b/packages/e2e/react-router/v7/cypress/e2e/shared/conditional-rendering.cy.ts
@@ -0,0 +1,11 @@
+import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryState',
+ hook: 'useQueryState'
+})
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryStates',
+ hook: 'useQueryStates'
+})
diff --git a/packages/e2e/react/cypress/e2e/shared/conditional-rendering.cy.ts b/packages/e2e/react/cypress/e2e/shared/conditional-rendering.cy.ts
new file mode 100644
index 000000000..333517391
--- /dev/null
+++ b/packages/e2e/react/cypress/e2e/shared/conditional-rendering.cy.ts
@@ -0,0 +1,11 @@
+import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryState',
+ hook: 'useQueryState'
+})
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryStates',
+ hook: 'useQueryStates'
+})
diff --git a/packages/e2e/react/src/routes.tsx b/packages/e2e/react/src/routes.tsx
index 3cd6f61fb..953fdb5a1 100644
--- a/packages/e2e/react/src/routes.tsx
+++ b/packages/e2e/react/src/routes.tsx
@@ -21,6 +21,8 @@ const routes: Record JSX.Element>> = {
'/form/useQueryStates': lazy(() => import('./routes/form.useQueryStates')),
'/referential-stability/useQueryState': lazy(() => import('./routes/referential-stability.useQueryState')),
'/referential-stability/useQueryStates': lazy(() => import('./routes/referential-stability.useQueryStates')),
+ '/conditional-rendering/useQueryState': lazy(() => import('./routes/conditional-rendering.useQueryState')),
+ '/conditional-rendering/useQueryStates': lazy(() => import('./routes/conditional-rendering.useQueryStates')),
'/render-count/useQueryState/true/replace/false': lazy(() => import('./routes/render-count')),
'/render-count/useQueryState/true/replace/true': lazy(() => import('./routes/render-count')),
diff --git a/packages/e2e/react/src/routes/conditional-rendering.useQueryState.tsx b/packages/e2e/react/src/routes/conditional-rendering.useQueryState.tsx
new file mode 100644
index 000000000..42089b432
--- /dev/null
+++ b/packages/e2e/react/src/routes/conditional-rendering.useQueryState.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryState
diff --git a/packages/e2e/react/src/routes/conditional-rendering.useQueryStates.tsx b/packages/e2e/react/src/routes/conditional-rendering.useQueryStates.tsx
new file mode 100644
index 000000000..8715144e1
--- /dev/null
+++ b/packages/e2e/react/src/routes/conditional-rendering.useQueryStates.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryStates
diff --git a/packages/e2e/remix/app/routes/conditional-rendering.useQueryState.tsx b/packages/e2e/remix/app/routes/conditional-rendering.useQueryState.tsx
new file mode 100644
index 000000000..42089b432
--- /dev/null
+++ b/packages/e2e/remix/app/routes/conditional-rendering.useQueryState.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryState } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryState
diff --git a/packages/e2e/remix/app/routes/conditional-rendering.useQueryStates.tsx b/packages/e2e/remix/app/routes/conditional-rendering.useQueryStates.tsx
new file mode 100644
index 000000000..8715144e1
--- /dev/null
+++ b/packages/e2e/remix/app/routes/conditional-rendering.useQueryStates.tsx
@@ -0,0 +1,3 @@
+import { ConditionalRenderingUseQueryStates } from 'e2e-shared/specs/conditional-rendering'
+
+export default ConditionalRenderingUseQueryStates
diff --git a/packages/e2e/remix/cypress/e2e/shared/conditional-rendering.cy.ts b/packages/e2e/remix/cypress/e2e/shared/conditional-rendering.cy.ts
new file mode 100644
index 000000000..333517391
--- /dev/null
+++ b/packages/e2e/remix/cypress/e2e/shared/conditional-rendering.cy.ts
@@ -0,0 +1,11 @@
+import { testConditionalRendering } from 'e2e-shared/specs/conditional-rendering.cy'
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryState',
+ hook: 'useQueryState'
+})
+
+testConditionalRendering({
+ path: '/conditional-rendering/useQueryStates',
+ hook: 'useQueryStates'
+})
diff --git a/packages/e2e/shared/specs/conditional-rendering.cy.ts b/packages/e2e/shared/specs/conditional-rendering.cy.ts
new file mode 100644
index 000000000..148ee80e6
--- /dev/null
+++ b/packages/e2e/shared/specs/conditional-rendering.cy.ts
@@ -0,0 +1,22 @@
+import { createTest } from '../create-test'
+
+export const testConditionalRendering = createTest(
+ 'Conditional rendering',
+ ({ path }) => {
+ it('should have the correct initial state after mounting', () => {
+ cy.visit(path + '?test=pass')
+ cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
+ cy.get('button#mount').click()
+ cy.get('#state').should('have.text', 'pass')
+ })
+ it('should keep the correct state after unmounting and remounting', () => {
+ cy.visit(path)
+ cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
+ cy.get('button#mount').click()
+ cy.get('button#set').click()
+ cy.get('button#unmount').click()
+ cy.get('button#mount').click()
+ cy.get('#state').should('have.text', 'pass')
+ })
+ }
+)
diff --git a/packages/e2e/shared/specs/conditional-rendering.tsx b/packages/e2e/shared/specs/conditional-rendering.tsx
new file mode 100644
index 000000000..9c6e3ce53
--- /dev/null
+++ b/packages/e2e/shared/specs/conditional-rendering.tsx
@@ -0,0 +1,60 @@
+'use client'
+
+import { parseAsString, useQueryState, useQueryStates } from 'nuqs'
+import { type FC, useState } from 'react'
+
+function ConditionalRenderer({ Component }: { Component: FC }) {
+ const [mounted, setMounted] = useState(false)
+ return (
+ <>
+
+
+ {mounted && }
+ >
+ )
+}
+
+// --
+
+function TestComponentUseQueryState() {
+ const [state, setState] = useQueryState('test')
+ return (
+ <>
+
+ {state}
+ >
+ )
+}
+
+function TestComponentUseQueryStates() {
+ const [{ state }, setState] = useQueryStates(
+ {
+ state: parseAsString
+ },
+ { urlKeys: { state: 'test' } }
+ )
+ return (
+ <>
+
+ {state}
+ >
+ )
+}
+
+// --
+
+export function ConditionalRenderingUseQueryState() {
+ return
+}
+
+export function ConditionalRenderingUseQueryStates() {
+ return
+}
diff --git a/packages/nuqs/src/adapters/lib/react-router.ts b/packages/nuqs/src/adapters/lib/react-router.ts
index 408b2df60..828db7e4f 100644
--- a/packages/nuqs/src/adapters/lib/react-router.ts
+++ b/packages/nuqs/src/adapters/lib/react-router.ts
@@ -20,8 +20,7 @@ type NavigateOptions = {
}
type NavigateFn = (url: NavigateUrl, options: NavigateOptions) => void
type UseNavigate = () => NavigateFn
-
-type UseSearchParams = () => [URLSearchParams, {}]
+type UseSearchParams = (initial: URLSearchParams) => [URLSearchParams, {}]
// --
@@ -78,7 +77,11 @@ export function createReactRouterBasedAdapter(
}
}
function useOptimisticSearchParams() {
- const [serverSearchParams] = useSearchParams()
+ const [serverSearchParams] = useSearchParams(
+ typeof location === 'undefined'
+ ? new URLSearchParams()
+ : new URLSearchParams(location.search)
+ )
const [searchParams, setSearchParams] = useState(serverSearchParams)
useEffect(() => {
function onPopState() {