Read a URL Query Parameter
Reading `?q=hello` is a one-liner with `URLSearchParams`, but the helpers around it (multi-value params, defaults, type coercion, updating without reload) are where most apps end up duplicating code. This snippet covers the basic read, a typed-default helper, and a setter that updates the URL with `history.replaceState` so the back button keeps working. Use it for filters, search inputs, and shareable links.
416 views
12
function getQueryParam(name, source = typeof location !== 'undefined' ? location.search : '') {
const params = new URLSearchParams(source);
return params.get(name);
}
console.log(getQueryParam('q', '?q=hello&page=2')); // hello
console.log(getQueryParam('missing', '?q=hello')); // null
console.log(getQueryParam('q', '?q=hello&q=world')); // hello (first)URLSearchParams parses any query-style string and exposes a Map-like API. Calling .get(name) returns the first occurrence as a string or null when the key is missing. Defaulting source to location.search lets the helper run anywhere a URL string can be passed (server-side requests, parsed URL objects, hash routes), which makes it easy to test without a real location. Note that .get always returns the first occurrence; for repeated keys (?tag=a&tag=b), use .getAll(name).
function getParam(name, fallback, source = '?') {
const params = new URLSearchParams(source);
const raw = params.get(name);
if (raw === null) return fallback;
if (typeof fallback === 'number') {
const n = Number(raw);
return Number.isFinite(n) ? n : fallback;
}
if (typeof fallback === 'boolean') return raw === 'true' || raw === '1';
return raw;
}
console.log(getParam('page', 1, '?page=3')); // 3
console.log(getParam('page', 1, '?page=oops')); // 1 (NaN guard)
console.log(getParam('debug', false, '?debug=1')); // true
console.log(getParam('q', '', '?q=cats')); // catsURL params are always strings, but call sites usually want numbers, booleans, or fall-throughs to a default. Inferring the target type from typeof fallback keeps the API tidy: callers pass the default they want and get back the same type. Guarding against NaN with Number.isFinite prevents ?page=oops from silently producing NaN in math downstream. Treating 'true' and '1' as truthy and everything else as falsy matches the convention used by most form-encoded URLs and feature-flag links.
function setQueryParam(name, value, source = '/?') {
// Build a base URL so URLSearchParams + URL play nicely without window.location.
const url = new URL(source, 'https://example.test');
if (value === null || value === undefined) url.searchParams.delete(name);
else url.searchParams.set(name, String(value));
return url.search;
// In a browser: history.replaceState(null, '', `${location.pathname}${url.search}`);
}
console.log(setQueryParam('q', 'cats', '/search?page=2'));
console.log(setQueryParam('q', null, '/search?q=cats&page=2'));URL plus URLSearchParams is the right combo for safe, encoding-aware query mutation: it handles already-encoded characters, repeated keys, and the leading ? correctly. Passing null/undefined to delete a key keeps the API symmetric with reactive form state where clearing a filter should drop the param entirely. In a real browser, swap the return for history.replaceState(null, '', url.search) so the URL updates without re-rendering the page or pushing a new history entry. Use pushState instead when the change should be back-button-navigable (route, page number).
