The Next.js App Router adapter enables nuqs to work seamlessly with Next.js 14.2.0 and above using the App Router.
Installation
Install nuqs
First, install nuqs in your Next.js project: Add the adapter to your root layout
Wrap your application with the NuqsAdapter in your root layout file:import { NuqsAdapter } from 'nuqs/adapters/next/app'
import type { ReactNode } from 'react'
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<NuqsAdapter>{children}</NuqsAdapter>
</body>
</html>
)
}
The adapter must be placed inside the <body> tag and wrap all content that uses nuqs hooks.
Use nuqs hooks in your client components
Now you can use useQueryState and useQueryStates in any client component:'use client'
import { useQueryState } from 'nuqs'
export default function SearchComponent() {
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 are client-side only and don’t trigger server re-renders:
const [state, setState] = useQueryState('key')
// Updates only happen on the client
Server Re-renders
Opt into server-side rendering updates by setting shallow: false:
const [state, setState] = useQueryState('key', { shallow: false })
// URL updates will trigger server component re-renders
Transitions
Combine with React’s useTransition for loading states during server updates:
'use client'
import { useTransition } from 'react'
import { useQueryState, parseAsString } from 'nuqs'
export function SearchWithLoading() {
const [isLoading, startTransition] = useTransition()
const [query, setQuery] = useQueryState(
'q',
parseAsString.withOptions({
shallow: false,
startTransition
})
)
return (
<div>
<input
value={query || ''}
onChange={(e) => setQuery(e.target.value)}
disabled={isLoading}
/>
{isLoading && <span>Loading...</span>}
</div>
)
}
How It Works
The Next.js App Router adapter:
- Uses
useSearchParams() from next/navigation to read the current URL state
- Uses
useOptimistic() to provide instant UI updates
- Calls
router.replace() for non-shallow updates to trigger server re-renders
- Patches the History API to detect navigation events and reset internal queues
- Batches multiple state updates efficiently
Server Components
For accessing search params in Server Components, use createSearchParamsCache:
import { createSearchParamsCache, parseAsString } from 'nuqs/server'
export const searchParamsCache = createSearchParamsCache({
q: parseAsString.withDefault('')
})
export default async function Page({
searchParams
}: {
searchParams: Promise<Record<string, string | string[] | undefined>>
}) {
const { q } = await searchParamsCache.parse(searchParams)
return <div>Search: {q}</div>
}
See the Server-Side Parsing documentation for more details.
Troubleshooting
Adapter must be inside <body>
The adapter relies on browser APIs and must be rendered inside the <body> tag. Placing it in <head> or outside <html> will cause errors.
Not working with Next.js 14.1 or older
The App 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
Search params not updating
Make sure:
- The component using nuqs hooks has the
'use client' directive
- The component is a child of the
NuqsAdapter
- You’re not accidentally using the wrong adapter (e.g., pages router adapter)