Guide: URL State Management with nuqs

Purpose

This guide covers how we use nuqs for type-safe URL state management in Next.js 15+ applications. It provides server-side URL parameter parsing with seamless client-side integration.

Key Principles

1. Framework Adapter Required

  • Always wrap your app with NuqsAdapter from nuqs/adapters/next
  • Required for Next.js 15+ compatibility

2. Server-First Approach

  • Parse URL parameters on the server using createSearchParamsCache
  • Always await searchParams in Next.js 15+ page components
  • Use Promise.all for concurrent async operations

3. Type Safety

  • Define parsers with proper types and defaults
  • Use appropriate parser types: parseAsString, parseAsInteger, parseAsBoolean
  • Always provide default values

4. Hybrid Usage

  • Share parser configurations between server and client components
  • Use useQueryStates hook for client-side parameter manipulation

Implementation Overview

Setup

Install and configure the framework adapter:

pnpm add nuqs@latest

Basic Pattern

1. Create Search Params Configuration

// app/lib/search-params.ts
export const searchParamsCache = createSearchParamsCache({
  page: parseAsString.withDefault("1"),
  filter: parseAsBoolean.withDefault(false),
});

2. Use in Page Components

// app/page.tsx
export default async function Page(props: { searchParams: Promise<any> }) {
  const searchParams = await props.searchParams;
  await searchParamsCache.parse(searchParams);
  const params = searchParamsCache.all();
  
  return <YourComponent params={params} />;
}

3. Client-Side Updates

// client-component.tsx
"use client";
export function ClientComponent() {
  const [params, setParams] = useQueryStates(parsers);
  // Update URL state as needed
}

Best Practices

URL Structure

  • Use short, semantic parameter names
  • Consider URL key mapping for better UX: pageSizesize
  • Keep URLs readable and shareable

Type Safety

  • Always use appropriate parsers for data types
  • Add validation constraints when needed
  • Provide meaningful default values

Performance

  • Initialize cache at the page level only
  • Use concurrent operations with Promise.all
  • Handle async operations properly in Next.js 15+

Common Patterns

Filtering and Pagination

const searchParamsCache = createSearchParamsCache({
  page: parseAsString.withDefault("1"),
  pageSize: parseAsString.withDefault("10"),
  search: parseAsString.withDefault(""),
});

Boolean Toggles

const searchParamsCache = createSearchParamsCache({
  showArchived: parseAsBoolean.withDefault(false),
  sortDesc: parseAsBoolean.withDefault(false),
});

Breaking Changes in nuqs 2

  • Framework Adapters: Must wrap app with NuqsAdapter
  • Default Behaviors: clearOnDefault is now true by default
  • JSON Parser: Requires validation function
  • Testing: Use NuqsTestingAdapter for tests

Key Takeaways

  1. Always use framework adapter - Required for Next.js 15+
  2. Server-first parsing - Parse parameters on server, use on client
  3. Type safety matters - Use proper parsers and defaults
  4. Keep URLs clean - Use semantic names and consider key mapping
  5. Handle async properly - Await searchParams and use Promise.all

This approach gives us type-safe, server-rendered URL state that works seamlessly with client-side interactions.