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 React adapter enables nuqs to work with plain React applications (SPAs) built with tools like Vite, Create React App, or any other React setup without a framework router.
Installation
Install nuqs
Install nuqs in your React project: Wrap your app with the adapter
Import and wrap your application with NuqsAdapter in your root entry file:import { NuqsAdapter } from 'nuqs/adapters/react'
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(
<NuqsAdapter>
<App />
</NuqsAdapter>
)
The adapter must wrap your entire application to provide the necessary context for nuqs hooks.
Use nuqs hooks in your components
Now you can use useQueryState and useQueryStates anywhere in your app:src/components/Search.tsx
import { useQueryState } from 'nuqs'
export function Search() {
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
- React:
>=18.2.0 or ^19.0.0-0
Features
History Syncing
By default, the React adapter only syncs with nuqs internal updates. To enable syncing with external History API updates, use enableHistorySync():
import { NuqsAdapter, enableHistorySync } from 'nuqs/adapters/react'
import { createRoot } from 'react-dom/client'
import App from './App'
enableHistorySync()
createRoot(document.getElementById('root')!).render(
<NuqsAdapter>
<App />
</NuqsAdapter>
)
This enables useOptimisticSearchParams to react to third-party code that directly updates the History API.
Full Page Navigation on Shallow False
By default, setting shallow: false uses the History API. To trigger full page navigations instead:
import { NuqsAdapter } from 'nuqs/adapters/react'
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(
<NuqsAdapter fullPageNavigationOnShallowFalseUpdates={true}>
<App />
</NuqsAdapter>
)
With this option enabled:
const [state, setState] = useQueryState('key', { shallow: false })
// Will trigger location.assign() or location.replace()
// instead of history.pushState() or history.replaceState()
Browser Back/Forward Support
The adapter automatically listens to popstate events, so the browser’s back and forward buttons work out of the box:
const [count, setCount] = useQueryState('count', parseAsInteger, {
history: 'push' // Each update creates a new history entry
})
// User can navigate back/forward through count changes
How It Works
The React adapter:
- Reads search params directly from
location.search
- Updates the URL using the History API (
history.pushState / history.replaceState)
- Listens to
popstate events for back/forward navigation
- Uses an internal event emitter to sync state across components
- Batches and throttles URL updates to avoid rate limiting
Usage with React Router
If you’re using React Router, use the dedicated React Router adapters instead:
These adapters integrate more deeply with React Router’s navigation system.
Examples
Search with Debouncing
import { useQueryState, parseAsString } from 'nuqs'
import { useState, useEffect } from 'react'
export function DebouncedSearch() {
const [search, setSearch] = useQueryState('q', parseAsString)
const [localValue, setLocalValue] = useState(search || '')
useEffect(() => {
const timer = setTimeout(() => {
setSearch(localValue || null)
}, 300)
return () => clearTimeout(timer)
}, [localValue, setSearch])
return (
<input
value={localValue}
onChange={(e) => setLocalValue(e.target.value)}
placeholder="Search..."
/>
)
}
Filters with Multiple States
import { useQueryStates, parseAsString, parseAsInteger } from 'nuqs'
export function ProductFilters() {
const [filters, setFilters] = useQueryStates({
category: parseAsString,
minPrice: parseAsInteger,
maxPrice: parseAsInteger,
inStock: parseAsBoolean
})
return (
<div>
<select
value={filters.category || ''}
onChange={(e) => setFilters({ category: e.target.value || null })}
>
<option value="">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
{/* More filter controls */}
<button onClick={() => setFilters({
category: null,
minPrice: null,
maxPrice: null,
inStock: null
})}>
Clear All Filters
</button>
</div>
)
}
Troubleshooting
State not syncing across components
Make sure all components using nuqs hooks are children of the NuqsAdapter.
URL not updating
Check that:
- Your app is running in a browser environment (not SSR)
- The
NuqsAdapter is properly mounted
- You’re not accidentally calling
preventDefault() or similar on navigation events
History API errors in Safari
Safari has stricter rate limits on History API calls. If you’re seeing errors, increase the throttle time:
const [state, setState] = useQueryState('key', {
throttleMs: 340 // Safari-safe throttle time
})