Logo
Published on

Query Parameters

How React Router Query Parameters Manage State

React Router Query Parameters store application state in the URL, enabling shareable links, bookmarkable pages, and persistent filter states. They provide a reliable way to sync UI state with the browser's address bar and support deep linking. Teams using query parameters report better user experience and easier state management.

TL;DR

  • Use useSearchParams to read and update URL parameters
  • Store filter states, pagination, and search terms in the URL
  • Enable shareable links and bookmarkable page states
  • Perfect for search pages, data tables, and filtered lists
const result = process(data)

The URL State Management Challenge

Your application's filter states, search terms, and pagination settings reset when users refresh the page or share links. Component state doesn't persist across browser sessions, making it impossible to bookmark specific search results or application states.

// Problematic: Filter state lost on page refresh
const React = require('react')
function ProductSearch() {
  const state = {
    searchTerm: 'laptops',
    category: 'electronics',
    sortBy: 'price',
  }

  const handleSearch = (newFilters) => {
    console.log('Search filters updated:', newFilters)
    return { ...state, ...newFilters } // Lost on refresh!
  }
  console.log('Filters not in URL - cannot bookmark')
  return { currentFilters: state, updateFilters: handleSearch }
}

React Router Query Parameters provide persistent, shareable state through URL management:

// React Router: Persistent filter state with URL parameters
const { useSearchParams } = require('react-router-dom')
function useProductFilters() {
  const [searchParams, setSearchParams] = useSearchParams()
  const filters = {
    searchTerm: searchParams.get('q') || '',
    category: searchParams.get('category') || 'all',
    sortBy: searchParams.get('sort') || 'relevance',
  }

  const updateFilters = (newFilters) => {
    setSearchParams(new URLSearchParams(searchParams))
  }
  console.log('Filters stored in URL for sharing/bookmarking')
  return { filters, updateFilters }
}

Best Practises

Use React Router query parameters when:

  • ✅ Building search pages with filters and sorting options
  • ✅ Creating data tables with pagination and column sorting
  • ✅ Implementing shareable links for specific application states
  • ✅ Storing user preferences that should persist across sessions

Avoid when:

  • 🚩 Sensitive data that shouldn't appear in URLs
  • 🚩 Large objects or complex nested data structures
  • 🚩 Temporary UI state like modal visibility or form validation
  • 🚩 Frequent state changes that would clutter browser history

System Design Trade-offs

AspectURL Query ParametersComponent State
PersistenceExcellent - survives page refreshPoor - lost on reload
ShareabilityGood - links include full statePoor - cannot share state
BookmarkingBuilt-in - URLs save stateManual - requires localStorage
Deep LinkingNative - direct access to statesComplex - custom routing logic
SEOGood - search engines index paramsPoor - dynamic content invisible
PrivacyMedium - visible in URL and logsGood - stays in browser memory

More Code Examples

❌ Manual URL parameter handling
// Manual URL parameter parsing and management
function manageSearchFilters() {
  const url = new URL(window.location)
  const params = url.searchParams

  function getFilters() {
    const filters = {}
    params.forEach((value, key) => {
      if (key === 'price_range') {
        const [min, max] = value.split(',')
        filters.priceMin = parseInt(min) || 0
        filters.priceMax = parseInt(max) || 1000
      } else if (key === 'categories') {
        filters.categories = value.split(',').filter(Boolean)
      } else {
        filters[key] = value
      }
    })
    console.log('Manual parameter parsing:', filters)
    return filters
  }

  function updateFilters(newFilters) {
    const newUrl = new URL(window.location)
    const newParams = newUrl.searchParams

    Object.entries(newFilters).forEach(([key, value]) => {
      if (key === 'priceMin' || key === 'priceMax') {
        const currentMin = newParams.get('price_min') || 0
        const currentMax = newParams.get('price_max') || 1000
        const min = key === 'priceMin' ? value : currentMin
        const max = key === 'priceMax' ? value : currentMax
        newParams.set('price_range', `${min},${max}`)
      } else if (Array.isArray(value)) {
        newParams.set(key, value.join(','))
      } else if (value !== null && value !== '') {
        newParams.set(key, value)
      } else {
        newParams.delete(key)
      }
    })

    window.history.replaceState({}, '', newUrl)
    console.log('Manual URL update required re-render trigger')
  }

  const currentFilters = getFilters()
  console.log('Complex manual parameter management')
  return { filters: currentFilters, updateFilters }
}
✅ useSearchParams hook
// React Router useSearchParams for clean parameter management
const { useSearchParams } = require('react-router-dom')
function useAdvancedFilters() {
  const [searchParams, setSearchParams] = useSearchParams()

  const filters = {
    query: searchParams.get('q') || '',
    category: searchParams.getAll('category') || [],
    priceMin: parseInt(searchParams.get('price_min')) || 0,
    priceMax: parseInt(searchParams.get('price_max')) || 1000,
    sortBy: searchParams.get('sort') || 'relevance',
    page: parseInt(searchParams.get('page')) || 1,
    itemsPerPage: parseInt(searchParams.get('per_page')) || 20,
  }

  const updateFilters = (updates) => {
    setSearchParams((prev) => {
      const newParams = new URLSearchParams(prev)

      Object.entries(updates).forEach(([key, value]) => {
        if (key === 'category' && Array.isArray(value)) {
          newParams.delete('category')
          value.forEach((cat) => newParams.append('category', cat))
        } else if (value !== null && value !== '' && value !== 0) {
          const paramKey =
            key === 'query'
              ? 'q'
              : key === 'priceMin'
                ? 'price_min'
                : key === 'priceMax'
                  ? 'price_max'
                  : key === 'itemsPerPage'
                    ? 'per_page'
                    : key
          newParams.set(paramKey, value)
        } else {
          const paramKey = key === 'query' ? 'q' : key
          newParams.delete(paramKey)
        }
      })

      return newParams
    })
  }

  const clearFilters = () => {
    setSearchParams({})
  }

  const getShareableUrl = () => {
    const url = new URL(window.location)
    console.log('Shareable URL with filters:', url.toString())
    return url.toString()
  }

  console.log('Current filters from URL:', filters)
  console.log('Automatic React re-renders on parameter changes')

  return { filters, updateFilters, clearFilters, getShareableUrl }
}

Technical Trivia

The Amazon Filter State Mystery of 2019: Amazon's mobile team noticed users frequently abandoned searches after applying filters, only to discover the issue was filter state loss on page refresh. Users would spend time setting complex filters, accidentally refresh, and lose all their selections, leading to frustration and cart abandonment.

Why component-only state failed: Search filters were stored in React component state without URL synchronization. Users couldn't bookmark filtered results, share specific searches with others, or maintain their filter selections across browser sessions, creating a poor user experience.

URL query parameters solved persistence: Implementing useSearchParams ensured filter state persisted across page refreshes and enabled shareable search URLs. Users could now bookmark specific search results, and the team saw a 35% increase in search engagement and reduced abandonment rates.


Master Query Parameters: Implementation Strategy

Store filter states, search terms, pagination settings, and user preferences in URL parameters for better user experience. Use meaningful parameter names and consider URL length limitations for complex filters. Always provide default values when reading parameters, and clean up empty or null values when updating. Remember that query parameters are visible to users and search engines, so avoid sensitive information.