typescriptintermediate

Search Params in Server and Client Components

Handle URL search params in both server and client components with type-safe parsing.

typescript
// Server Component — searchParams from page props
// app/products/page.tsx
interface ProductsPageProps {
  searchParams: Promise<{
    category?: string;
    sort?: string;
    page?: string;
    q?: string;
  }>;
}

export default async function ProductsPage({ searchParams }: ProductsPageProps) {
  const params = await searchParams;
  const category = params.category || 'all';
  const sort = params.sort || 'newest';
  const page = parseInt(params.page || '1', 10);
  const query = params.q || '';

  const products = await fetchProducts({ category, sort, page, query });

  return (
    <div>
      <h1>Products - {category}</h1>
      <p>Sort: {sort}, Page: {page}</p>
      <ProductGrid products={products} />
      <Pagination currentPage={page} />
    </div>
  );
}

// Client Component — useSearchParams
'use client';

import { useSearchParams, useRouter, usePathname } from 'next/navigation';
import { useCallback } from 'react';

export function Filters() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const pathname = usePathname();

  const updateParam = useCallback(
    (key: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString());
      if (value) params.set(key, value);
      else params.delete(key);
      router.push(`${pathname}?${params.toString()}`);
    },
    [searchParams, router, pathname]
  );

  return (
    <select
      value={searchParams.get('sort') || 'newest'}
      onChange={(e) => updateParam('sort', e.target.value)}
    >
      <option value="newest">Newest</option>
      <option value="price-asc">Price: Low to High</option>
      <option value="price-desc">Price: High to Low</option>
    </select>
  );
}

Use Cases

  • product filters
  • paginated lists
  • search pages

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.