Documentation Index
Fetch the complete documentation index at: https://mintlify.com/47ng/nuqs/llms.txt
Use this file to discover all available pages before exploring further.
The Next.js Pages Router adapter enables nuqs to work with Next.js 14.2.0 and above using the Pages Router (the traditional pages/ directory structure).
Installation
Install nuqs
First, install nuqs in your Next.js project: Add the adapter to _app.tsx
Wrap your application with the NuqsAdapter in your custom App component:import type { AppProps } from 'next/app'
import { NuqsAdapter } from 'nuqs/adapters/next/pages'
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<NuqsAdapter>
<Component {...pageProps} />
</NuqsAdapter>
)
}
Use nuqs hooks in your pages
Now you can use useQueryState and useQueryStates in any page component:import { useQueryState } from 'nuqs'
export default function SearchPage() {
const [search, setSearch] = useQueryState('q')
return (
<div>
<input
value={search || ''}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
<p>Search query: {search || 'none'}</p>
</div>
)
}
Version Requirements
- Next.js:
>=14.2.0
- React:
>=18.2.0 or ^19.0.0-0
For Next.js versions older than 14.2.0, use nuqs v1.x instead, which doesn’t require the adapter setup.
Features
Shallow Updates (Default)
By default, URL updates don’t trigger getServerSideProps to re-run:
const [state, setState] = useQueryState('key')
// Shallow update - no server request
Server-Side Updates
Opt into re-running getServerSideProps by setting shallow: false:
const [state, setState] = useQueryState('key', { shallow: false })
// This will trigger getServerSideProps to re-run
Dynamic Routes
The adapter automatically handles dynamic route segments, preserving them during URL updates:
import { useQueryState, parseAsInteger } from 'nuqs'
import { useRouter } from 'next/router'
export default function PostPage() {
const router = useRouter()
const { id } = router.query
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1))
return (
<div>
<h1>Post {id}</h1>
<p>Page {page}</p>
<button onClick={() => setPage(page + 1)}>Next Page</button>
</div>
)
}
The adapter correctly separates dynamic route parameters (id) from search params (page).
How It Works
The Pages Router adapter:
- Uses
useRouter() from next/compat/router to access the router
- Reads search params from
router.query
- Updates the URL using
router.push() or router.replace()
- Automatically extracts and preserves dynamic route segments
- Listens to
routeChangeStart and beforeHistoryChange events to reset queues
Server-Side Rendering
You can access and parse search params in getServerSideProps:
import { createLoader, parseAsInteger, parseAsString } from 'nuqs/server'
import type { GetServerSideProps } from 'next'
const searchParams = {
q: parseAsString,
page: parseAsInteger.withDefault(1)
}
const loadSearchParams = createLoader(searchParams)
export const getServerSideProps: GetServerSideProps = async (context) => {
const { q, page } = loadSearchParams(context.query)
// Fetch data using the parsed search params
const products = await fetchProducts({ query: q, page })
return {
props: { products, q, page }
}
}
export default function ProductsPage({ products, q, page }) {
// You can also use the hooks on the client side
const [query, setQuery] = useQueryState('q')
return (
<div>
<h1>Products</h1>
<p>Query: {q || 'none'}, Page: {page}</p>
{/* Render products */}
</div>
)
}
Catch-All Routes
The adapter supports catch-all and optional catch-all routes:
// URL: /docs/getting-started/installation?ref=home
// router.query.slug = ['getting-started', 'installation']
// search params: ref=home
const [ref, setRef] = useQueryState('ref')
// Works correctly alongside dynamic segments
Troubleshooting
Search params conflict with dynamic route segments
If you have a search param with the same name as a dynamic segment, the dynamic segment takes precedence:
// pages/user/[id].tsx
// URL: /user/123?id=456
// router.query.id will be '123' (from the route)
// The search param 'id=456' is ignored
Avoid naming conflicts by using different names for route segments and search params.
getServerSideProps not re-running
Make sure you’re passing shallow: false when updating state:
setState('value', { shallow: false })
Not working with Next.js 14.1 or older
The Pages Router adapter requires Next.js 14.2.0 or newer. For older versions:
- Upgrade to Next.js 14.2.0+, or
- Use nuqs v1.x which has built-in support for older Next.js versions