Glossary

URLSearchParams

URLSearchParams is the standard JavaScript class that parses and builds a URL query string as an ordered list of name and value pairs, so you never split on ampersands by hand. It is defined by the WHATWG URL Standard and exists in browsers, workers and Node.js. A leading question mark in the input is ignored, and the same name may appear more than once.

How it works

You create one from a string, an object, or an array of pairs. Internally it is a list, not a map, so duplicates and order are kept. Both strings and numbers are accepted as values, but everything is converted to a string on the way in.

  • get(name) returns the first value or null, and getAll(name) returns an array of every value.
  • append adds a pair at the end. set replaces the first match and deletes the other matches.
  • has(name) tests for a name, and newer engines also accept has(name, value). delete works the same way. The size property counts pairs.
  • sort() orders pairs by name, comparing UTF-16 code units, so uppercase letters sort before lowercase.
  • toString() serializes in application/x-www-form-urlencoded form, with no leading question mark.

Decoding turns + into a space and decodes percent sequences as UTF-8. Encoding writes a space as +. That differs from encodeURIComponent, which writes %20.

const p = new URLSearchParams('?tag=a&tag=b&q=hello+world&e=&flag');
console.log(p.get('tag'), p.getAll('tag'), p.get('q'), JSON.stringify(p.get('flag')), p.get('missing'));
p.append('tag', 'c'); p.set('q', 'a b'); console.log(p.toString());
console.log(new URLSearchParams({ a: 1, b: undefined, c: null, d: [1, 2] }).toString());
console.log(new URLSearchParams({ s: "a b~!*'()-._é" }).toString());
const s = new URLSearchParams('b=2&a=1&B=3&a=0'); s.sort(); console.log(s.toString());
console.log(Object.fromEntries(new URLSearchParams('a=1&a=2&b=3')));

Output from Node.js 22.22.0:

a [ 'a', 'b' ] hello world "" null
tag=a&tag=b&q=a+b&e=&flag=&tag=c
a=1&b=undefined&c=null&d=1%2C2
s=a+b%7E%21*%27%28%29-._%C3%A9
B=3&a=1&a=0&b=2
{ a: '2', b: '3' }

What is the difference between get and getAll?

get returns only the first value for a name, while getAll returns an array of all values. For ?tag=a&tag=b, get('tag') is "a" and getAll('tag') is ["a", "b"]. A missing name gives null from get and an empty array from getAll. A bare name such as flag gives an empty string, not null, so test with has when you need presence.

Does URLSearchParams use + or %20 for spaces?

It writes + for a space and reads + back as a space. This is the form-encoding rule, and servers that follow the same rule decode it correctly. If an API wants %20, build the string yourself with encodeURIComponent, or replace + after calling toString. A literal plus sign in a value is written as %2B, so 1+2 survives a round trip.

Common pitfalls

  • Arrays and objects: an array value becomes one comma-joined string, "1,2", not repeated keys. Use append in a loop for repeated names. An undefined or null value is written as the text "undefined" or "null".
  • Object.fromEntries drops duplicates: the last value wins, as the output above shows. Use getAll when a name can repeat.
  • Expecting a leading question mark: toString never includes it. Add it yourself when you build a URL by hand, or assign the object to a URL's search.
  • Unusual characters: the form encoding also escapes the exclamation mark, apostrophe, parentheses and tilde, which encodeURIComponent leaves alone. A signature computed over the raw query string will differ between the two encoders.
  • Malformed percent sequences: a bad sequence such as %zz is kept as typed instead of throwing.

Related terms

  • URL API — the URL class whose searchParams property returns a live URLSearchParams
  • Query string — the text after the question mark that this class parses
  • URL encoding — the percent-encoding rules used when serializing
  • Fetch API — accepts a URLSearchParams object as a form-encoded request body
  • Base64 — a different encoding sometimes confused with query escaping

See also