i11 UIi11 registry

Query Strings

Lightweight hooks for managing URL query parameters in Next.js with type safety, defaults, and validation.

Key Features

  • Type-safe query parameter management
  • Automatic default value handling
  • Optional Zod schema validation
  • Batched updates for multiple parameters

Install

You can directly install this into your codebase using shadcn:

pnpx shadcn@latest add https://i11-registry.vercel.app/r/use-query-state.json

useQueryString

Manages a single URL query parameter with optional defaults and validation.

Parameters

ParameterTypeDescription
keystringThe name of the query parameter to track
optionsUseQueryStateOptionsOptional configuration object

Returns

NameTypeDescription
Current valuestring | null | undefinedThe current query parameter value, or null/undefined if not present and no default provided
Setter function(value: string) => voidUpdates the query parameter using client-side navigation

Basic Usage

function SearchPage() {
  const [query, setQuery] = useQueryString('q');
  
  return <div> ... </div>;
}

With Default Value

function ProductList() {
  const [sort, setSort] = useQueryString('sort', {
    defaultValue: 'name'
  });
  // URL: /products?sort=price → sort will be 'price'
  // URL: /products → sort will be 'name' → /products?sort=name
  
  return <div> ... </div>;
}

With Validation Schema

import { z } from 'zod';

function FilteredList() {
  const [status, setStatus] = useQueryString('status', {
    defaultValue: 'active',
    schema: z.enum(['active', 'inactive', 'pending'])
  });
  
  // Invalid values will fall back to 'active'
  // URL: /items?status=waiting → status will be 'active'
  
  return <div> ... </div>;
}

useQueryStrings

Manages multiple URL query parameters simultaneously with batched updates.

Parameters

ParameterTypeDescription
keysstring[]Array of unique query parameter names to track
optionsRecord<string, UseQueryStateOptions>Per-key configuration options

Returns

NameTypeDescription
Current valuesRecord<string, string | null | undefined>Object mapping each key to its current value
Setter function(key: string, value: string) => voidUpdates a specific query parameter using client-side navigation

Basic Usage

function SearchFilters() {
  const [filters, setFilter] = useQueryStrings(['sort', 'category', 'minPrice']);

  console.log(filters.sort);
  // name
  
  return <div> ... </div>;
}

With Per-Key Options

function AdvancedFilters() {
  const [filters, setFilter] = useQueryStrings(
    ['page', 'sort'],
    {
      page: {
        defaultValue: '1',
        schema: z.coerce.number().min(1).transform(String)
      },
      sort: {
        defaultValue: 'name',
        schema: z.enum(['name', 'price', 'date'])
      }
    }
  );
  
  return <div> ... </div>;
}

Options Config

type UseQueryStateOptions = {
  defaultValue?: string;
  schema?: z.ZodType<string>;
  onParseError?: "fallback" | "remove";
}
OptionTypeDefaultDescription
defaultValuestringundefinedFallback value when parameter is missing, empty, or fails validation
schemaz.ZodType<string>undefinedZod schema for validating parameter values (must return a string)
onParseError"fallback" | "remove""fallback"Behavior when validation fails: use defaultValue or remove parameter

Schema Validation

The onParseError option controls what happens when schema validation fails:

  • "fallback" (default)

Uses defaultValue if provided, otherwise removes the parameter.

const [status, setStatus] = useQueryString('status', {
  defaultValue: 'active',
  schema: z.enum(['active', 'inactive']),
  onParseError: 'fallback'
});

// URL: /page?status=invalid → status becomes 'active'
  • "remove" Always removes the parameter on validation failure.
const [status, setStatus] = useQueryString('status', {
  schema: z.enum(['active', 'inactive']),
  onParseError: 'remove'
});

// URL: /page?status=invalid → parameter is removed from URL

Validation schemas must return a string to maintain URLSearchParams compatibility:

// ✅ Valid schemas
z.string()
z.enum(['option1', 'option2'])
z.coerce.number().transform(String)
z.string().regex(/pattern/)

// ❌ Invalid schemas (don't return strings)
z.number()
z.boolean()
z.object({})

Notes

React Compiler Assumption

These hooks are not memoized internally. They assume you're using the React Compiler, which automatically optimizes component re-renders.

Null vs Empty String

  • When no query parameter exists and no default is provided, the value will be null
  • Setting a value to an empty string ('') removes the parameter from the URL
  • Default values are applied when parameters are missing, empty, or fail validation

Query Parameter Deduplication

Duplicate query parameters (e.g., ?step=goal&step=persona) are not officially supported. While the hooks may deduplicate in some cases, this behavior should not be relied upon.