jsguides

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.

See Also

  • fetch() — network requests that accept URL query strings
  • URL — the searchParams property on the URL interface
  • Promise — async operations that often receive URLSearchParams-backed URLs
  • Map — another key/value structure, but without URL query string semantics