High-level pattern hooks for nuqs - pagination, filtering, sorting, and more
npm install nuqs-presets> High-level pattern hooks for nuqs - Stop reinventing pagination, filtering, sorting, and search.



nuqs is an excellent library for managing URL state, but building common patterns like pagination, filtering, and sorting still requires boilerplate. nuqs-presets provides ready-to-use hooks that solve these patterns with best practices baked in.
``tsx`
// 30+ lines of boilerplate for pagination alone
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1))
const [pageSize, setPageSize] = useQueryState('pageSize', parseAsInteger.withDefault(10))
// ... handle navigation, validation, edge cases ...
`tsx`
// 2 lines, everything handled
const { page, pageSize, nextPage, prevPage, hasNextPage } = usePagination()
- ✅ 7 production-ready hooks - Pagination, filtering, sorting, search, tabs, date ranges, multi-select
- ✅ Type-safe - Full TypeScript support with excellent inference
- ✅ Zero config - Sensible defaults for immediate use
- ✅ Customizable - Override any behavior when needed
- ✅ Tiny - Tree-shakeable, minimal bundle size
- ✅ Framework agnostic - Works anywhere nuqs works (Next.js, Remix, React Router, etc.)
`bash`
npm install nuqs-presets nuqsor
pnpm add nuqs-presets nuqsor
yarn add nuqs-presets nuqsor
bun add nuqs-presets nuqs
Requirements:
- nuqs ^2.0.0react
- ^18.3.0 or ^19.0.0
Follow the nuqs setup guide for your framework:
`tsx
// Next.js App Router - app/layout.tsx
import { NuqsAdapter } from 'nuqs/adapters/next/app'
export default function RootLayout({ children }) {
return (
$3
`tsx
'use client'import { usePagination, useFilters, useSorting } from 'nuqs-presets'
import { parseAsString, parseAsFloat } from 'nuqs'
const filterParsers = {
category: parseAsString,
minPrice: parseAsFloat,
}
export function ProductList() {
const { page, pageSize, nextPage, prevPage, hasNextPage, hasPrevPage } = usePagination()
const { filters, setFilter, clearFilters } = useFilters({
parsers: filterParsers,
})
const { sortBy, sortOrder, toggleSort } = useSorting({
columns: ['name', 'price', 'date'] as const
})
return (
{/ Your UI /}
Page {page}
)
}
`Hooks
$3
Complete pagination with all the bells and whistles.
`tsx
const {
page, // Current page (1-indexed)
pageSize, // Items per page
totalPages, // Total pages (computed)
hasNextPage, // Can go forward
hasPrevPage, // Can go back
nextPage, // Go to next page
prevPage, // Go to previous page
goToPage, // Go to specific page
setPageSize, // Change page size
} = usePagination({
defaultPageSize: 10,
totalItems: 1000,
})
`$3
Type-safe filter management with nuqs parsers.
`tsx
import { parseAsString, parseAsFloat } from 'nuqs'const filterParsers = {
category: parseAsString,
minPrice: parseAsFloat,
maxPrice: parseAsFloat,
inStock: parseAsBoolean,
}
const {
filters, // Current filters (type-safe)
setFilter, // Set a single filter
clearFilters, // Clear all filters
hasFilters, // Any filters active?
} = useFilters({
parsers: filterParsers,
})
// filters.category is string | null
// filters.minPrice is number | null
`$3
Smart column sorting with toggle behavior.
`tsx
const {
sortBy, // Current sort column
sortOrder, // 'asc' | 'desc' | null
toggleSort, // Toggle column (null → asc → desc → null)
isSortedBy, // Check if column is sorted
} = useSorting({
columns: ['name', 'date', 'price'] as const
})
`$3
Debounced search with min length validation.
`tsx
const {
query, // Current search query
debouncedQuery, // Debounced value for API calls
setQuery, // Update search
isDebouncing, // Debounce in progress
} = useSearch({
debounce: 300,
minLength: 2,
})
`$3
Type-safe tab navigation.
`tsx
const {
activeTab, // Current tab (type-safe)
setTab, // Change tab
isActive, // Check if tab is active
} = useTabs(['overview', 'analytics', 'settings'] as const)
`$3
Date range selection with presets.
`tsx
const {
startDate, // Start date
endDate, // End date
setRange, // Set both dates
presets, // Quick presets (last 7 days, etc.)
} = useDateRange({
defaultPreset: 'last7days'
})
`$3
Array-based multi-selection.
`tsx
const {
selected, // Selected items
toggle, // Toggle selection
selectAll, // Select all
deselectAll, // Deselect all
} = useMultiSelect({
allItems: ['item1', 'item2', 'item3']
})
`API Reference
For detailed API documentation for each hook, see the source code or TypeScript definitions. All hooks are fully typed with JSDoc comments.
Live Examples
This repository includes working example applications in the
examples/ directory. These are complete, runnable apps that demonstrate real-world usage.$3
#### nextjs-basic - ✅ Complete
A simple Next.js 16 app demonstrating basic usage with:
- Pagination with page size control
- Debounced search
- Multi-column sorting
- Clean, modern UI with dark mode support
Run it:
`bash
cd examples/nextjs-basic
npm run dev
`
Open http://localhost:3000#### nextjs-ecommerce - 🧪 Beta
Advanced e-commerce filtering interface with:
- Multi-faceted filtering (category, price range, brand)
- Filter badges with clear functionality
- Type-safe parsers with nuqs
Run it:
`bash
cd examples/nextjs-ecommerce
npm run dev
`
Open http://localhost:3000#### nextjs-dashboard - 🧪 Beta
Admin dashboard demonstrating:
- Tab-based navigation
- Data tables with all features
- Date range filtering
Run it:
`bash
cd examples/nextjs-dashboard
npm run dev
`
Open http://localhost:3000#### react-vite - 📦 Coming Soon
Framework-agnostic React SPA with:
- Vite for fast development
- React Router integration
- Client-side routing
See the examples README for more details.
Code Examples
$3
`tsx
'use client'import { usePagination, useFilters, useSorting, useSearch } from 'nuqs-presets'
import { parseAsString, parseAsFloat, parseAsBoolean } from 'nuqs'
const filterParsers = {
category: parseAsString,
minPrice: parseAsFloat,
maxPrice: parseAsFloat,
inStock: parseAsBoolean,
}
export function ProductList() {
const { page, pageSize, setPage, hasNextPage, hasPrevPage } = usePagination({
defaultPageSize: 24,
totalItems: 1000,
})
const { filters, setFilter, clearFilters, hasFilters } = useFilters({
parsers: filterParsers,
})
const { sortBy, sortOrder, toggleSort } = useSorting({
columns: ['name', 'price', 'rating'] as const,
defaultColumn: 'name',
defaultOrder: 'asc',
})
const { query, debouncedQuery, setQuery } = useSearch({
debounce: 300,
minLength: 2,
})
// Fetch products with all filters
const { data: products } = useProducts({
page,
pageSize,
...filters,
sortBy,
sortOrder,
search: debouncedQuery,
})
return (
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search products..."
/>
{products?.map((product) => (
{product.name}
))}
Page {page}
)
}
`Tree-shaking
All hooks are tree-shakeable. Import only what you need:
`tsx
// Import individual hooks
import { usePagination } from 'nuqs-presets/pagination'
import { useFilters } from 'nuqs-presets/filtering'
import { useSorting } from 'nuqs-presets/sorting'
`TypeScript
All hooks are fully typed with excellent type inference. No need to manually specify types in most cases:
`tsx
const { activeTab } = useTabs(['overview', 'analytics', 'settings'] as const)
// activeTab is typed as 'overview' | 'analytics' | 'settings'const filterParsers = {
category: parseAsString,
minPrice: parseAsFloat,
}
const { filters } = useFilters({
parsers: filterParsers,
})
// filters.category is typed as string | null
// filters.minPrice is typed as number | null
``Contributions are welcome! Please read our Contributing Guide first.
MIT © Hitesh Agrawal
Built on top of the excellent nuqs by François Best.
---
⭐ If you find this useful, please star the repo!