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.

BySourav Mishra3 min read

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?

  1. Shareability: Users can copy-paste the URL to share their exact view.
  2. Persistence: Refreshing the page keeps the state intact.
  3. 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: 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)

You might also like

See all