$ 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.

JavaScript
/** "?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

Try it
Result
Ad slot