Stop Using useState for Filters (Use URL State) - By Sourav Mishra (@souravvmishra)
Why you should store table filters and search inputs in the URL. A comprehensive guide to using 'nuqs' in Next.js for better UX and shareability.
We've all been there. You filter a dashboard to show "Active Users" sorted by "Date". You refresh the page, and boom—everything resets. You copy the link to send to a colleague, but it just opens the default view.
This happens because you're using useState when you should be using URL State.
In this guide, I, Sourav Mishra, Co-founder of Codestam Technologies, explain why the URL should be your single source of truth and how identifying this pattern increased our SaaS product's shareability by 200%.
Why URL State?
- Shareability: Users can copy-paste the URL to share their exact view.
- Persistence: Refreshing the page keeps the state intact.
- Browser History: The "Back" button actually works for undoing filters.
Enter nuqs (Next.js URL Query Strings)
Handling searchParams manually in Next.js Client Components can be tricky. nuqs creates a type-safe hook that feels exactly like useState.
Installation
npm install nuqs
Usage Example
Let's say we have a search input.
❌ The useState Way (Bad):
const [search, setSearch] = useState('');
✅ The nuqs Way (Good):
'use client';
import { useQueryState } from 'nuqs';
export function SearchBar() {
const [search, setSearch] = useQueryState('q', { defaultValue: '' });
return (
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
);
}
Now, typing "hello" updates the URL to ?q=hello automatically.
Advanced Parsers
nuqs comes with built-in parsers for boolean, integers, and JSON.
```tsx
import { parseAsInteger, useQueryState, parseAsJson } from 'nuqs';
// URL: ?page=2
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
// URL: ?filter={"role":"admin","active":true}
const [filter, setFilter] = useQueryState('filter', parseAsJson({ role: 'user', active: false }));
At Codestam Technologies, using parseAsJson allowed us to store complex multi-select filters directly in the URL without implementing a custom Redux store.
## Server-Side Access
The beauty of URL state is that Server Components can read it too!
```tsx
// app/page.tsx
export default function Page({ searchParams }: { searchParams: { q: string } }) {
const query = searchParams.q;
// Fetch data based on query...
}
Key Takeaways
- URL = Truth: If it's not in the URL, it didn't happen.
- Better UX: Your users will thank you for shareable links.
- Type Safety:
nuqshandles serialization/deserialization for you.
For more on modern React patterns, read about the React 19 Compiler.
This guide was written by Sourav Mishra, Co-founder of Codestam Technologies and a Full Stack Engineer.