stop using useState for filters (use url state instead) - By Sourav Mishra (@souravvmishra)

okeyy, so let's see why storing search filters in the url is way better than useState.

BySourav Mishra3 min read

we've all been there. you filter a table on a dashboard, refresh the page, and boom—everything resets back to default. or you try to share the link with a friend and they see a blank screen.

that happens because you used useState when you should have used URL state.

in this guide, i, sourav mishra, will show you why the url should be your single source of truth and how nuqs makes it super easy.


why url state?

  1. shareable: users can copy-paste the exact url to show someone else.
  2. saves state: refreshing the page doesn't reset your filters.
  3. back button works: hitting the browser back button undoes the filter.

meet nuqs

handling query parameters manually in next.js can get annoying. nuqs gives you a type-safe hook that feels just like useState.

npm install nuqs
import { useQueryState } from 'nuqs';

export function SearchBox() {
  const [search, setSearch] = useQueryState('q', { defaultValue: '' });

  return (
    <input 
      value={search} 
      onChange={(e) => setSearch(e.target.value)} 
      placeholder="search..." 
    />
  );
}

stop losing state on page refresh. use nuqs and keep your filters in the url.

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: nuqs handles 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.

Share this post

Cover image for stop using useState for filters (use url state instead)

You might also like

See all