Keep repeated query parameters when editing a URL
URLSearchParams supports repeated keys, but set() and Object.fromEntries() can quietly discard them. Use getAll() and append() when a filter accepts multiple values.
A filter URL might look like this:
/products?tag=javascript&tag=testing&sort=newest
The two `tag` entries are intentional: the user selected two filters. Trouble starts when code treats the query string like an object with one value per key.
const url = new URL(
'https://example.com/products?tag=javascript&tag=testing&sort=newest'
);
console.log(url.searchParams.get('tag'));
// javascript
console.log(Object.fromEntries(url.searchParams));
// { tag: 'testing', sort: 'newest' }
Those operations lose information in different ways. `get()` returns the first matching value. Building an object keeps the last value for a repeated key because an object property can hold only one value at a time.
The useful shift is to treat query parameters as a list of name/value pairs. Some names can appear more than once.
Replace one filter without rebuilding the whole query
Use `getAll()` to read every value. To replace a selection, delete that key’s existing entries and append the new values:
function replaceTags(input, tags) {
const url = new URL(input);
url.searchParams.delete('tag');
for (const tag of tags) {
url.searchParams.append('tag', tag);
}
return url;
}
const next = replaceTags(
'https://example.com/products?tag=javascript&tag=testing&sort=newest',
['accessibility', 'react']
);
console.log(next.searchParams.getAll('tag'));
// [ 'accessibility', 'react' ]
console.log(next.searchParams.get('sort'));
// newest
console.log(next.href);
// https://example.com/products?sort=newest&tag=accessibility&tag=react
Run this in a browser console or Node.js. Then try passing `[]`: both tag entries disappear, while `sort` stays intact.
The gotcha is `set()`. Calling `set('tag', 'react')` replaces the first matching entry and removes the other entries with that name. That’s documented behavior, and it’s useful for a single-value parameter such as `sort`. It won’t preserve a multi-select filter.
Repeated keys are my preference here because `URLSearchParams` handles them directly. Your API still needs to define what they mean: whether tags use AND or OR matching, whether duplicates matter, and whether order matters. The URL API preserves the values; it doesn't decide your filtering rules.
One tradeoff: editing through `searchParams` can normalize encoding and change the serialized query string. Use this approach for ordinary application URLs. If a URL’s signature depends on its exact query text, follow that signing scheme’s rules before modifying it.