← Back to all hooks

useQueryParams

Sync URL search params with component state. Perfect for filters, pagination, and shareable application states.

Live Demo

Try changing the filters below and watch the URL update in real-time. The state persists across page refreshes and browser navigation!

šŸ’” Tip: Watch the URL in your browser's address bar change as you interact with the filters below. Try using the browser's back/forward buttons!

šŸ” Search & Filter

to
1

šŸ“Š Current State

Search: (empty)
Category: (all)
Price Range: $0 - $1000
In Stock: No
Page: 1
šŸ”— Current URL:
Loading...
šŸ’” Try This:
  • Change filters and watch the URL update
  • Copy the URL and open in a new tab
  • Use browser back/forward buttons
  • Refresh the page - state persists!

šŸŽÆ Quick Actions

šŸ’” Features Demonstrated:

  • Automatic URL sync with component state
  • Type-safe query parameters (string, number, boolean)
  • Browser back/forward navigation support
  • State persistence across page refreshes
  • Shareable URLs with current state
  • Individual and bulk parameter updates

Code Examples

Basic Usage

import { useQueryParams } from "@uiblock/hooks";

interface Filters {
  search: string;
  page: number;
  sort: string;
}

export default function ProductList() {
  const { params, setParam } = useQueryParams<Filters>({
    search: '',
    page: 1,
    sort: 'name'
  });

  return (
    <div>
      <input
        value={params.search}
        onChange={(e) => setParam('search', e.target.value)}
        placeholder="Search..."
      />
      <select
        value={params.sort}
        onChange={(e) => setParam('sort', e.target.value)}
      >
        <option value="name">Name</option>
        <option value="price">Price</option>
      </select>
      <div>Page: {params.page}</div>
    </div>
  );
}

Search Filters

import { useQueryParams } from "@uiblock/hooks";

interface SearchFilters {
  query: string;
  category: string;
  minPrice: number;
  maxPrice: number;
  inStock: boolean;
}

export default function SearchPage() {
  const { params, setParam, clearParams } = useQueryParams<SearchFilters>({
    query: '',
    category: '',
    minPrice: 0,
    maxPrice: 1000,
    inStock: false
  });

  return (
    <div>
      <input
        value={params.query}
        onChange={(e) => setParam('query', e.target.value)}
      />
      <select
        value={params.category}
        onChange={(e) => setParam('category', e.target.value)}
      >
        <option value="">All</option>
        <option value="electronics">Electronics</option>
        <option value="books">Books</option>
      </select>
      <label>
        <input
          type="checkbox"
          checked={params.inStock}
          onChange={(e) => setParam('inStock', e.target.checked)}
        />
        In Stock Only
      </label>
      <button onClick={clearParams}>Clear Filters</button>
    </div>
  );
}

Pagination

import { useQueryParams } from "@uiblock/hooks";

interface PaginationParams {
  page: number;
  limit: number;
}

export default function DataTable() {
  const { params, setParam, setParams } = useQueryParams<PaginationParams>({
    page: 1,
    limit: 10
  });

  const nextPage = () => setParam('page', params.page + 1);
  const prevPage = () => setParam('page', Math.max(1, params.page - 1));
  const changeLimit = (limit: number) => {
    setParams({ page: 1, limit }); // Reset to page 1 when changing limit
  };

  return (
    <div>
      <select
        value={params.limit}
        onChange={(e) => changeLimit(Number(e.target.value))}
      >
        <option value={10}>10 per page</option>
        <option value={25}>25 per page</option>
        <option value={50}>50 per page</option>
      </select>
      
      <button onClick={prevPage} disabled={params.page === 1}>
        Previous
      </button>
      <span>Page {params.page}</span>
      <button onClick={nextPage}>Next</button>
    </div>
  );
}

With Replace Mode

import { useQueryParams } from "@uiblock/hooks";

export default function SearchBox() {
  // Use replace: true to avoid cluttering browser history
  const { params, setParam } = useQueryParams(
    { search: '' },
    { replace: true }
  );

  return (
    <input
      value={params.search}
      onChange={(e) => setParam('search', e.target.value)}
      placeholder="Search (won't add to history)..."
    />
  );
}

Remove Individual Params

import { useQueryParams } from "@uiblock/hooks";

export default function Filters() {
  const { params, setParam, removeParam } = useQueryParams({
    search: '',
    category: '',
    tag: ''
  });

  return (
    <div>
      {params.search && (
        <div>
          Search: {params.search}
          <button onClick={() => removeParam('search')}>āœ•</button>
        </div>
      )}
      {params.category && (
        <div>
          Category: {params.category}
          <button onClick={() => removeParam('category')}>āœ•</button>
        </div>
      )}
    </div>
  );
}

API Reference

Parameters

defaultParams: T

Default values for query parameters. Types are inferred from these values.

options?: UseQueryParamsOptions

Optional configuration object

  • replace: Use replaceState instead of pushState (default: false)
  • debounce: Debounce URL updates in milliseconds (default: 0)

Return Value

params: T

Current query parameters synced with URL

setParam: (key, value) => void

Update a single parameter

setParams: (params) => void

Update multiple parameters at once

removeParam: (key) => void

Remove a parameter (resets to default value)

clearParams: () => void

Reset all parameters to default values

šŸ’” Use Cases

  • Search and filter interfaces
  • Pagination controls
  • Sorting and ordering
  • Tab navigation
  • Form state persistence
  • Shareable application states
  • Deep linking to specific views