Web APIsEasy

Extracting query string values in JavaScript

01

The Short Answer

The modern way to extract query string values is the URLSearchParams API — it parses the query string into a map-like object with methods like get(), getAll(), has(), and entries(). You create it from window.location.search or from any URL string. Before URLSearchParams existed, developers manually split the query string on & and = characters or used regex. The modern API handles edge cases automatically — URL decoding, duplicate keys, empty values, and special characters.

02

URLSearchParams — The Modern Approach

URLSearchParams is a built-in class that provides a clean interface for reading and manipulating query strings. You pass it the search string (with or without the leading ?) and it gives you methods to access individual parameters, iterate over all of them, or check if a parameter exists. It also handles URL encoding/decoding automatically.

url-search-params.tstypescript
// URL: https://example.com/search?query=javascript&page=2&sort=date&tag=react&tag=typescript

// Create from window.location.search
const params = new URLSearchParams(window.location.search);

// Get a single value
params.get('query');  // 'javascript'
params.get('page');   // '2' (always a string!)
params.get('missing'); // null (not found)

// Check if a parameter exists
params.has('sort');   // true
params.has('limit');  // false

// Get all values for a key (handles duplicates)
params.getAll('tag'); // ['react', 'typescript']

// Iterate over all parameters
for (const [key, value] of params.entries()) {
  console.log(`${key}: ${value}`);
}
// query: javascript
// page: 2
// sort: date
// tag: react
// tag: typescript

// Convert to a plain object (loses duplicate keys)
const paramsObject = Object.fromEntries(params.entries());
// { query: 'javascript', page: '2', sort: 'date', tag: 'typescript' }
// ⚠️ Only keeps the LAST value for duplicate keys

// URL decoding is automatic
const encoded = new URLSearchParams('?name=John%20Doe&city=New%20York');
encoded.get('name'); // 'John Doe' (decoded automatically)
encoded.get('city'); // 'New York'
03

Creating from a Full URL

If you have a full URL string (not just the query part), use the URL constructor first to parse it, then access its searchParams property. This is cleaner than manually extracting the query string portion and handles edge cases like hash fragments that come after the query string.

url-constructor.tstypescript
// Parse a full URL and access its query parameters
const url = new URL('https://shop.example.com/products?category=shoes&color=red&size=10#reviews');

// Access searchParams directly from the URL object
url.searchParams.get('category'); // 'shoes'
url.searchParams.get('color');    // 'red'
url.searchParams.get('size');     // '10'

// Other useful URL properties
url.hostname;   // 'shop.example.com'
url.pathname;   // '/products'
url.hash;       // '#reviews'
url.search;     // '?category=shoes&color=red&size=10'

// Modifying query parameters
url.searchParams.set('page', '2');        // Add/update a param
url.searchParams.append('color', 'blue'); // Add another value for 'color'
url.searchParams.delete('size');          // Remove a param
url.searchParams.sort();                  // Sort params alphabetically

console.log(url.toString());
// 'https://shop.example.com/products?category=shoes&color=red&color=blue&page=2#reviews'

// Building a URL with params from scratch
const apiUrl = new URL('https://api.example.com/search');
apiUrl.searchParams.set('q', 'hello world');  // Auto-encodes spaces
apiUrl.searchParams.set('limit', '20');
apiUrl.searchParams.set('offset', '0');

console.log(apiUrl.toString());
// 'https://api.example.com/search?q=hello+world&limit=20&offset=0'
04

Manual Parsing (Legacy Approach)

Before URLSearchParams, developers parsed query strings manually by splitting on & and =. This approach is error-prone — you need to handle URL decoding, missing values, duplicate keys, and edge cases like + representing spaces. It's shown here for understanding, but you should always use URLSearchParams in modern code.

manual-parsing.tstypescript
// ❌ Legacy approach — manual parsing (don't use in new code)
function parseQueryString(queryString: string): Record<string, string> {
  const params: Record<string, string> = {};

  // Remove leading '?' if present
  const cleaned = queryString.startsWith('?') ? queryString.slice(1) : queryString;
  if (!cleaned) return params;

  // Split on '&' and process each pair
  cleaned.split('&').forEach((pair) => {
    const [rawKey, rawValue = ''] = pair.split('=');
    const key = decodeURIComponent(rawKey);
    const value = decodeURIComponent(rawValue.replace(/\+/g, ' '));
    params[key] = value;
  });

  return params;
}

// Issues with manual parsing:
// - Doesn't handle duplicate keys (tag=a&tag=b)
// - Easy to forget URL decoding
// - '+' as space is a special case
// - Empty values ('key=' vs 'key') need handling
// - Doesn't handle malformed input gracefully

// ✅ Modern approach — always prefer URLSearchParams
const params = new URLSearchParams(window.location.search);
// Handles ALL edge cases automatically
05

Common Patterns

In real applications, you often need to read params for filtering, pagination, or search. Here are common patterns for working with query parameters in a type-safe way, including converting string values to the types you actually need.

common-patterns.tstypescript
// Type-safe parameter extraction
function getSearchParams(url: string) {
  const params = new URL(url).searchParams;

  return {
    query: params.get('q') ?? '',
    page: Number(params.get('page')) || 1,
    limit: Number(params.get('limit')) || 20,
    sortBy: params.get('sort') ?? 'date',
    tags: params.getAll('tag'), // Array of all 'tag' values
    isActive: params.get('active') === 'true', // Boolean from string
  };
}

// Building query strings for API calls
function buildSearchUrl(filters: {
  query: string;
  page: number;
  tags: string[];
}): string {
  const params = new URLSearchParams();

  if (filters.query) params.set('q', filters.query);
  params.set('page', String(filters.page));
  filters.tags.forEach((tag) => params.append('tag', tag));

  return `/api/search?${params.toString()}`;
}

buildSearchUrl({ query: 'react hooks', page: 2, tags: ['react', 'hooks'] });
// '/api/search?q=react+hooks&page=2&tag=react&tag=hooks'

// Updating current URL without page reload (SPA pattern)
function updateUrlParams(updates: Record<string, string | null>) {
  const url = new URL(window.location.href);

  Object.entries(updates).forEach(([key, value]) => {
    if (value === null) {
      url.searchParams.delete(key);
    } else {
      url.searchParams.set(key, value);
    }
  });

  window.history.pushState({}, '', url.toString());
}
06

Why Interviewers Ask This

This question tests basic web API knowledge and whether you know the modern tools available. Interviewers want to see that you reach for URLSearchParams (not manual string splitting), understand URL encoding/decoding and why it matters, can handle edge cases like duplicate keys and missing values, know how to build query strings (not just read them), and understand the relationship between URL and URLSearchParams. It's a practical question — every web developer works with query strings regularly.

Quick Revision Cheat Sheet

Modern API: new URLSearchParams(window.location.search)

From full URL: new URL(urlString).searchParams

Read value: params.get('key') → string | null

Duplicate keys: params.getAll('key') → string[]

Build params: params.set('key', 'value'), params.append('key', 'value2')

To string: params.toString() → 'key=value&key2=value2'