$ snippet query-string-urlsearchparams
Parse and build query strings
Read ?page=2&tag=a&tag=b into an object, and build a URL from an object — with arrays, without hand-rolled encoding.
/** "?page=2&tag=a&tag=b" → { page: "2", tag: ["a", "b"] } */
function parseQuery(search = location.search) {
const out = {};
for (const [key, value] of new URLSearchParams(search)) {
if (key in out) out[key] = [].concat(out[key], value);
else out[key] = value;
}
return out;
}
/** { page: 2, tag: ["a","b"], q: "" , skip: undefined } → "page=2&tag=a&tag=b&q=" */
function buildQuery(params) {
const sp = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value === undefined || value === null) continue;
for (const v of Array.isArray(value) ? value : [value]) sp.append(key, String(v));
}
return sp.toString();
}
/** Update one param in the current URL without reloading the page. */
function setQueryParam(key, value) {
const url = new URL(location.href);
if (value === undefined || value === null || value === "") url.searchParams.delete(key);
else url.searchParams.set(key, value);
history.replaceState(null, "", url);
}How it works
URLSearchParams does the encoding and decoding correctly — including + as space, which decodeURIComponent gets wrong. Repeated keys are how HTML forms and most backends express arrays, so the parser collects them into an array only when a key appears more than once.
Gotchas
- Everything comes back as a string. Convert numbers and booleans yourself (
Number(q.page),q.debug === "true"). URLSearchParamsencodes spaces as+, which is right for query strings but not for path segments; useencodeURIComponentfor paths.- For a full URL,
new URL(href)gives you.searchParamsdirectly plus.pathname,.hashetc.
Ad slot