Iterating over params
So far, you’ve been reading params by name with get() and getAll(). But sometimes you don’t know all the key names ahead of time. Maybe a user sends a URL with a bunch of filter parameters and you want to process all of them. For that, you need to loop through the URLSearchParams.
Good news: URLSearchParams is iterable, which means you can loop over it just like an array.
for...of: the most common way
const params = new URLSearchParams("name=Alice&role=admin&theme=dark");
for (const [key, value] of params) {
console.log(`${key} → ${value}`);
}
// name → Alice
// role → admin
// theme → dark Each iteration gives you a [key, value] pair, which is an array of two strings. We use destructuring (const [key, value]) to grab both parts cleanly in one line. Without destructuring, you’d write const pair = ... and then access pair[0] and pair[1], which is less readable.
This is the approach you’ll use most of the time.
forEach(): the callback way
params.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// name: Alice
// role: admin
// theme: dark Let’s look at what that mistake looks like in practice:
// Wrong — the arguments are swapped
params.forEach((key, value) => {
console.log(key); // This is actually the VALUE, not the key!
});
// Correct
params.forEach((value, key) => {
console.log(key, value);
}); The code runs without errors either way. JavaScript doesn’t care what you name your variables. But if you name them (key, value), you’ll be printing values when you think you’re printing keys. It’s a silent bug. The for...of approach doesn’t have this problem because [key, value] destructuring matches the actual pair order.
keys(), values(), and entries()
These methods return iterators that you can spread into arrays:
const params = new URLSearchParams("a=1&b=2&c=3");
[...params.keys()]; // ["a", "b", "c"]
[...params.values()]; // ["1", "2", "3"]
[...params.entries()]; // [["a", "1"], ["b", "2"], ["c", "3"]] keys() gives you just the parameter names. values() gives you just the values. entries() gives you the [key, value] pairs, which is the same thing you get when you iterate over params directly with for...of.
The spread syntax [...] converts the iterator into a real array, so you can use array methods like .map(), .filter(), or .includes() on the result.
Converting to a plain object
Here’s a very handy trick. You can use Object.fromEntries() to turn URLSearchParams into a regular JavaScript object:
const params = new URLSearchParams("name=Alice&age=30&role=admin");
const obj = Object.fromEntries(params);
// { name: "Alice", age: "30", role: "admin" } Object.fromEntries() takes anything that produces [key, value] pairs and turns it into a plain object. Since URLSearchParams is iterable and yields [key, value] pairs, it works perfectly.
This is great when you want to pass search params to a function that expects a plain object, or when you want to destructure the values.
What do you think would happen if you convert "tag=js&tag=css&tag=html" to an object? You’d lose "js" and "css" entirely. Only "html" (the last value) would remain. This is a common trap when working with multi-value query strings.
Practical example: building a filter description
Let’s put iteration to work. Imagine you’re building an e-commerce API and want to log which filters the user applied:
const params = new URLSearchParams("color=red&size=M&brand=Nike&brand=Adidas");
const filters = [];
for (const [key, value] of params) {
filters.push(`${key}: ${value}`);
}
console.log("Active filters: " + filters.join(", "));
// "Active filters: color: red, size: M, brand: Nike, brand: Adidas" We create an empty filters array, then loop through every entry in params. For each one, we push a formatted string like "color: red" into the array. Finally, we join them all with commas into a single readable string.
Notice that brand appears twice because the user selected two brands. The iteration gives you every entry, including duplicates.
In the next lesson, you’ll see how URLSearchParams and the URL object work together through a live connection that keeps them in sync automatically.
In params.forEach((val, key) => ...), which argument comes first?