URLs don't have native array support, so different systems use different conventions. Understanding these formats helps you work with various APIs and frameworks.
Key Takeaways
- 1Repeated keys: ?tag=a&tag=b (widely supported)
- 2Bracket notation: ?tags[]=a&tags[]=b (PHP/Rails style)
- 3Comma-separated: ?tags=a,b,c (compact)
- 4Indexed brackets: ?tags[0]=a&tags[1]=b (explicit order)
- 5Check your API/framework docs for the expected format
Definition
“The application/x-www-form-urlencoded format is in many ways an aberrant monstrosity, the result of many years of implementation accidents and compromises leading to a set of requirements necessary for interoperability, but in no way representing good design practices.”— WHATWG URL Standard
Array Format Comparison
| Format | Example | Common In |
|---|---|---|
| Repeated keys | ?tag=a&tag=b&tag=c | URLSearchParams, Express |
| Empty brackets | ?tags[]=a&tags[]=b | PHP, Rails |
| Indexed brackets | ?tags[0]=a&tags[1]=b | PHP, qs library |
| Comma-separated | ?tags=a,b,c | REST APIs |
| JSON | ?tags=["a","b"] | Some APIs (needs encoding) |
Repeated Keys
https://api.example.com/products?category=electronics&category=computers&category=laptopsArray using repeated parameter keys
javascript
// JavaScript: URLSearchParams handles this naturally
const url = new URL('https://api.example.com/products');
url.searchParams.append('category', 'electronics');
url.searchParams.append('category', 'computers');
url.searchParams.append('category', 'laptops');
// Reading repeated values
const categories = url.searchParams.getAll('category');
// ['electronics', 'computers', 'laptops']
// Note: .get() only returns the first value
url.searchParams.get('category'); // 'electronics'Bracket Notation
https://api.example.com/products?tags[]=red&tags[]=blue&tags[]=greenPHP/Rails style bracket notation
javascript
// Building bracket-style parameters
const tags = ['red', 'blue', 'green'];
const params = tags.map(t => `tags[]=${encodeURIComponent(t)}`).join('&');
// "tags[]=red&tags[]=blue&tags[]=green"
// Parsing with qs library
import qs from 'qs';
const parsed = qs.parse('tags[]=red&tags[]=blue');
// { tags: ['red', 'blue'] }php
<?php
// PHP parses brackets automatically
$_GET['tags']; // ['red', 'blue', 'green']
// Building array URLs in PHP
$tags = ['red', 'blue', 'green'];
$query = http_build_query(['tags' => $tags]);
// "tags%5B0%5D=red&tags%5B1%5D=blue&tags%5B2%5D=green"
// (decoded: tags[0]=red&tags[1]=blue&tags[2]=green)Comma-Separated
https://api.example.com/products?tags=red,blue,greenComma-separated values (compact)
javascript
// Building
const tags = ['red', 'blue', 'green'];
const url = new URL('https://api.example.com/products');
url.searchParams.set('tags', tags.join(','));
// "?tags=red%2Cblue%2Cgreen" (URLSearchParams encodes commas;
// servers decode %2C back to "," before you split)
// Parsing
const tagsParam = url.searchParams.get('tags');
const tagsArray = tagsParam ? tagsParam.split(',') : [];
// ['red', 'blue', 'green']
// Watch out for values containing commas!
const genres = ['Rock', 'R&B, Soul', 'Hip-Hop'];
url.searchParams.set('genres', genres.join(','));
// genres=Rock%2CR%26B%2C+Soul%2CHip-Hop
// After decoding, splitting by comma would break "R&B, Soul"!Indexed Brackets
https://api.example.com/sort?fields[0]=name&fields[1]=date&order[0]=asc&order[1]=descIndexed brackets for ordered arrays
javascript
// Using qs library for complex cases
import qs from 'qs';
const data = {
fields: ['name', 'date', 'price'],
order: ['asc', 'desc', 'asc']
};
// encodeValuesOnly keeps [ ] and , readable; by default qs
// percent-encodes them (fields%5B0%5D=name, name%2Cdate)
qs.stringify(data, { arrayFormat: 'indices', encodeValuesOnly: true });
// "fields[0]=name&fields[1]=date&fields[2]=price&order[0]=asc&order[1]=desc&order[2]=asc"
qs.stringify(data, { arrayFormat: 'brackets', encodeValuesOnly: true });
// "fields[]=name&fields[]=date&fields[]=price&order[]=asc&order[]=desc&order[]=asc"
qs.stringify(data, { arrayFormat: 'repeat', encodeValuesOnly: true });
// "fields=name&fields=date&fields=price&order=asc&order=desc&order=asc"
qs.stringify(data, { arrayFormat: 'comma', encodeValuesOnly: true });
// "fields=name,date,price&order=asc,desc,asc"Framework-Specific Behavior
| Framework | Default Format | Notes |
|---|---|---|
| Express.js | Repeated keys | req.query.key returns array |
| PHP | Brackets | $_GET parses [] automatically |
| Rails | Brackets | params[:key] returns array |
| Django | Repeated keys | request.GET.getlist() |
| Spring | Repeated/comma | Configurable |
| ASP.NET | Repeated keys | Request.Query returns StringValues |