Guide
Repeated query string parameters: how to preserve every value
Learn how repeated parameters work, why plain objects lose values, and when to use getAll, append, or an ordered list.
by Tools in a Tab · Published on · Reviewed on
Short answer
A query string may repeat the same key, and every occurrence is a separate
entry: tag=astro&tag=seo. Preserve both with an ordered list of pairs or
URLSearchParams.getAll("tag"). Converting the query directly to a plain
{ tag: value } object commonly discards information because one property can
hold only one value at a time.
What a repeated key actually means
URL syntax does not require every name to appear once. This query contains three entries even though it has only two distinct keys:
?tag=astro&tag=seo&page=2
The observable order is tag=astro, tag=seo, then page=2. The WHATWG URL
Standard models URLSearchParams as a list of pairs, not a dictionary. As a
result, get("tag") returns the first value while getAll("tag") returns
['astro', 'seo']. The URLSearchParams API
also distinguishes append(), which adds another entry, from set(), which
replaces the first value and removes later entries with the same name.
Common ways to represent arrays
There is no universal array convention for query strings. Three APIs may require three different contracts:
tag=astro&tag=seo
tag[]=astro&tag[]=seo
tag=astro,seo
The first form repeats a key. The second includes square brackets literally in the key name. The third contains one comma-bearing value; the comma might be a separator or part of the data itself. Follow the receiving API’s documentation. Automatically converting between these forms can change meaning.
The query string parser displays one row per entry and rebuilds the list without guessing an array convention. Its JSON is an ordered list:
[
{ "key": "tag", "value": "astro" },
{ "key": "tag", "value": "seo" }
]
Spaces, plus signs, and empty values
URLSearchParams uses
application/x-www-form-urlencoded.
When parsed, + becomes a space and %2B becomes a literal plus. Therefore,
q=a+b&q=a%2Bb represents the values a b and a+b.
Empty values should also be preserved. flag and flag= both produce a name
flag with an empty string in URLSearchParams; whether an API treats their
original spellings differently depends on its own parser. An explicit empty
key such as =value is another list entry, although it may indicate a contract
mistake.
A safe JavaScript pattern
Iterate over every pair when order and duplicates matter:
const params = new URLSearchParams(location.search);
const entries = [...params.entries()];
const tags = params.getAll('tag');
Use append('tag', 'new') to add another value. Use set() only when you
deliberately want one occurrence. Avoid Object.fromEntries(params) when
duplicates matter: the resulting object retains only the last value for each
name.
Checklist
- Confirm how the receiver documents list parameters.
- Preserve order and duplicates while parsing.
- Distinguish a
+space from a%2Bliteral plus. - Decide explicitly whether empty values are allowed.
- Serialize from pairs, not from an object that has already lost data.