new URLSearchParams({ q: ["greeting", "foobar"] }).toString() returns q=greeting%2Cfoobar. The array was implicitly converted to a string, and two values became one comma-separated value. Write a filter into the URL without noticing, and the server receives a single entry.
This covers the basics of URLSearchParams and how to keep repeated keys intact. The Next.js App Router exposes a read-only interface through useSearchParams as well.
Creating URLSearchParams
You can construct URLSearchParams from a query string, a record, or an iterable of key-value pairs.
const empty = new URLSearchParams();
const fromString = new URLSearchParams("q=greeting");
const fromRecord = new URLSearchParams({ q: "greeting" });
const fromEntries = new URLSearchParams([["q", "greeting"]]);
An iterable such as Map works as well.
const map = new Map();
map.set("q", "greeting");
const params = new URLSearchParams(map);
toString() serializes the parameters without the leading question mark.
const params = new URLSearchParams("q=greeting");
params.toString();
// 'q=greeting'
Repeated keys are serialized as q=greeting&q=foobar. If a server specifically expects q[]=greeting&q[]=foobar, use q[] as the key.
const params = new URLSearchParams([
["q", "greeting"],
["q", "foobar"],
]);
params.toString();
// 'q=greeting&q=foobar'
Use append, delete, and set to modify values. get returns the first value for a key, while getAll returns every value. You can iterate with entries and sort by key with sort.
Converting an object with repeated keys
In the Next.js Pages Router, router.query can represent repeated keys as arrays.
{
q: ["greeting", "foobar"];
}
Passing that object directly produces the comma-separated string from the opening example.
const params = new URLSearchParams({ q: ["greeting", "foobar"] });
params.toString();
// 'q=greeting%2Cfoobar'
Convert the object to a list of string pairs instead.
const query = {
q: ["greeting", "foobar"],
r: "routing",
};
const entries: [string, string][] = [];
for (const [key, value] of Object.entries(query)) {
if (typeof value === "string") {
entries.push([key, value]);
} else {
value.forEach((item) => entries.push([key, item]));
}
}
const params = new URLSearchParams(entries);
When you already have the full URL, use the searchParams property of a URL instance.
const url = new URL("https://example.com?q=greeting&q=foobar&r=routing#ddd");
url.searchParams.toString();
// 'q=greeting&q=foobar&r=routing'
Closing note
The platform already provides capable URL APIs: URLSearchParams, URL, and URLPattern. Checking whether one of them fits before joining or splitting strings by hand removes a whole class of encoding and repeated-value bugs.