URLSearchParams
new URLSearchParams() URLSearchParams is a built-in JavaScript object that provides a consistent interface for reading and writing the query string portion of a URL. It landed in browsers with Chrome 51, Firefox 49, and Safari 10.1, and has been available in Node.js since version 10. The interface follows the WHATWG URL spec, which means behavior is consistent across environments — unlike the older querystring module that Node.js used to rely on.
You rarely construct URLSearchParams in isolation. The more common entry point is reading url.searchParams from a URL object, which gives you a live view of the URL’s query string. Changes you make through the URLSearchParams API write back through to the URL’s search property.
Syntax
new URLSearchParams()
new URLSearchParams("foo=bar&baz=qux")
new URLSearchParams({ foo: "bar", baz: "qux" })
new URLSearchParams([["foo", "bar"], ["baz", "qux"]])
new URLSearchParams(existingSearchParams)
The constructor accepts a wide range of input types. A string is parsed as a query string (decoding happens automatically). An object treats its enumerable string properties as key/value pairs. An array of two-element arrays works the same way. You can also pass another URLSearchParams instance to get a copy.
Constructor
When called with no arguments, you get an empty URLSearchParams object ready to be populated:
const params = new URLSearchParams();
params.append("page", "1");
params.toString();
// "page=1"
When you pass a string, the API parses it as a query string and decodes percent-encoded sequences:
const params = new URLSearchParams("name=Alice%26Bob&age=30");
params.get("name"); // "Alice&Bob" — decoded
params.get("age"); // "30"
Instance Methods
URLSearchParams has eleven instance methods that cover the full range of read, write, and iteration operations.
Reading values
get(name) returns the first value for a key, or null if the key is absent:
const params = new URLSearchParams("q=hello&q=world&q!");
params.get("q"); // "hello"
params.get("page"); // null
getAll(name) returns every value for a key as an array:
params.getAll("q"); // ["hello", "world", "!"]
has(name) tells you whether a key exists, and optionally checks for a specific value:
params.has("q"); // true
params.has("q", "hello"); // true
params.has("q", "world"); // true
params.has("other"); // false
Writing values
append(name, value) adds a new key/value pair. If the key already exists, it does not remove the existing entries — it adds another one:
const params = new URLSearchParams();
params.append("tag", "javascript");
params.append("tag", "es6");
params.getAll("tag"); // ["javascript", "es6"]
set(name, value) always replaces all existing values for that key:
const params = new URLSearchParams("tag=javascript&tag=es6");
params.set("tag", "ts");
params.getAll("tag"); // ["ts"]
delete(name, value?) removes entries. With only a name, it removes every entry for that key. With a value argument, it only removes entries that match both the name and value:
const params = new URLSearchParams("tag=js&tag=ts&tag=rust");
params.delete("tag", "ts");
// remaining: tag=js, tag=rust
params.delete("tag");
// all gone
sort() orders all keys in ascending code point order. Unlike a plain Map, URLSearchParams preserves insertion order — sort() is the only way to reorder:
const params = new URLSearchParams("z=1&a=2&m=3");
params.sort();
params.toString(); // "a=2&m=3&z=1"
toString() returns the query string in percent-encoded form, suitable for assigning to URL.search:
const params = new URLSearchParams();
params.append("name", "Alice & Bob");
params.append("q", "hello world");
params.toString();
// "name=Alice%20%26%20Bob&q=hello+world"
Iteration
keys(), values(), and entries() each return an iterator. Combined with for...of, this gives you a clean way to walk the params:
const params = new URLSearchParams("color=red&size=large&color=blue");
for (const key of params.keys()) {
console.log(key);
}
// color
// size
// color
for (const [key, value] of params.entries()) {
console.log(`${key} = ${value}`);
}
// color = red
// size = large
// color = blue
forEach(callback, thisArg?) iterates with the same data, passing (value, key, searchParams) to the callback:
params.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
The size property
The size property gives you the total number of key/value entries without needing to count manually:
const params = new URLSearchParams("a=1&b=2&c=3");
params.size; // 3
Supported in Node.js 16+, Chrome 101+, Firefox 128+, and Safari 16.4+. For older environments, fall back to params.keys().length.
Common Gotchas
Double encoding
If you pre-encode a value and then pass it to append() or set(), URLSearchParams will encode it again. A space becomes %2520 instead of %20. Always pass raw strings and let URLSearchParams handle encoding:
// wrong — double-encoded
params.append("q", encodeURIComponent("hello world"));
params.toString(); // "q=hello%2520world"
// correct — raw string
params.append("q", "hello world");
params.toString(); // "q=hello%20world"
Plus sign as space
In query string format, + represents a space. So parsing the string "bin=E+AXQB+A" will give you "E AXQB A" when you call get("bin"). This trips people up when working with Base64 data or unencoded + characters in values. Encode first if you need the + preserved:
const params = new URLSearchParams("bin=E+AXQB+A");
params.get("bin"); // "E AXQB A" — plus decoded to space
const params2 = new URLSearchParams();
params2.append("bin", encodeURIComponent("E+AXQB+A"));
params2.get("bin"); // "E%2BAXQB%2BA"
Decoding asymmetry between get() and toString()
get() returns a decoded value, but toString() returns a re-encoded string. This means round-tripping through toString() does not give you back the original stored representation — values are normalized on both read and write.
Iteration order is preserved
Unlike Map, URLSearchParams preserves the order in which keys were added (or as they appeared in the original query string). This matters when the order of parameters affects an API’s behavior — many REST APIs treat ?a=1&b=2 and ?b=2&a=1 as different requests.
Using with the URL interface
The URL interface exposes searchParams as a live URLSearchParams object:
const url = new URL("https://example.com/search?q=hello&page=2");
url.searchParams.get("q"); // "hello"
url.searchParams.set("page", "3");
url.search; // "?q=hello&page=3"
Modifying url.searchParams updates url.search and vice versa. This two-way binding is the most common way URLSearchParams is used in practice.