Form URL encoding 2026-11-05
Form URL encoding is the application/x-www-form-urlencoded format for serializing an ordered list of name/value pairs. It is used by URLSearchParams and common form-processing conventions. Repeated names are allowed; the data model need not be a dictionary with one value per name.[ref]
For example, the three pairs ("q", "a b+c"), ("tag", "x") and ("tag", "y") serialize as q=a+b%2Bc&tag=x&tag=y.
Here the space becomes +, while the literal plus in the value becomes %2B. Parsing the result recovers a b+c and both tag values. Replacing every plus with a space before deciding which encoding convention applies would misread data from other URL contexts.
Structural separators must also be distinguished from value data. The encoded pair q=a%26b has one value, a&b, rather than two pairs. The form parser first recognizes pair boundaries and then decodes their component data. Percent-encoding inside a component is consequently not an instruction to reinterpret the resulting characters as new separators.[ref]
For modern serialization, text is encoded as UTF-8 bytes before the relevant bytes are percent-encoded. The name/value pair ("word", "café") therefore produces word=caf%C3%A9. This connects a text character with its byte representation rather than assigning one arbitrary percent code to the whole character.[ref]
A query string can use this format, but generic URL syntax does not require every query to be interpreted this way. State the parser and encoding when comparing an original spelling with its recovered list of values.
URLSearchParams 2026-11-05
URLSearchParams is an API for representing and manipulating an ordered list of name/value pairs, commonly obtained from a URL's query string. Repeated names are permitted. It uses form URL encoding, rather than imposing its interpretation on every conceivable query syntax.[ref]
For new URLSearchParams('tag=a&tag=b&q=a+b'), getAll('tag') returns both a and b, while get('q') returns a b. Calling set('tag','c') replaces the repeated entries with one: toString() then returns tag=c&q=a+b. An associative array holding just one value for each name would discard the original repeated-field information.
A literal plus needs a different spelling. Appending the pair q and a+b to a URL's searchParams produces query ?q=a%2Bb. The plus in the data is represented by percent-encoding, whereas the plus in q=a+b represents a space under this parser. These examples distinguish a character in a value from a character used by the encoding convention.[ref]
A URL's searchParams object is connected to that URL; changes update its query. Constructing a separate URLSearchParams from a query string instead gives an independent object, useful when the original spelling must remain available for comparison.[ref]
URL serialization 2026-11-05
URL serialization produces the textual spelling of a structured URL record. It is the output side of processing that begins with a URL parser. The WHATWG serializer returns an ASCII string and can optionally omit the fragment identifier.[ref]
A worked example separates the stages. Constructing new URL('https://EXAMPLE.org:443/a/../b?q=hello%20world~#top') gives href equal to https://example.org/b?q=hello%20world~#top. Lowercasing the host, removing the default port and resolving the parent-directory segment happen during parsing; the serializer exposes the resulting record.
Now call u.searchParams.sort() on that object. Even though there is only one query pair to sort, href becomes https://example.org/b?q=hello+world%7E#top. The decoded query value is unchanged, but URLSearchParams has rewritten its spelling using form URL encoding. Thus changing the query through an API can alter its percent-encoding without changing the value returned by that API.[ref]
A serialized address is also different from a browser's chosen display text or a server's returned content. Comparing those different outputs answers different questions.