Which characters get encoded
A URL may only contain a limited set of ASCII characters. Everything else, including spaces, non-English letters and emoji, is first turned into UTF-8 bytes, and each byte is written as % plus two hexadecimal digits. Characters that have a job in a URL, such as ?, &, =, / and #, must also be encoded when they appear inside a value.
| Character | Encoded | Why |
|---|---|---|
| space | %20 (or + in forms) | Not allowed in URLs |
& | %26 | Separates query parameters |
= | %3D | Separates name and value |
? | %3F | Starts the query string |
# | %23 | Starts the fragment |
/ | %2F | Separates path segments |
% | %25 | Starts an escape |
é | %C3%A9 | Two UTF-8 bytes |
In code
encodeURIComponent('café & croissants'); // caf%C3%A9%20%26%20croissants
decodeURIComponent('caf%C3%A9'); // café
// Building a query string safely
new URLSearchParams({ q: 'café & croissants' }).toString(); // q=caf%C3%A9+%26+croissantsFrequently asked questions
What is the difference between encodeURI and encodeURIComponent?
encodeURIComponent encodes everything except letters, digits and - _ . ! ~ * ' ( ), so it is the one to use for a single query parameter value or path segment. encodeURI leaves characters with meaning in a full URL, such as : / ? # & =, untouched, so it is only for encoding a whole URL that is already structured correctly.
Should a space be %20 or +?
In a URL path, a space must be %20. In a query string sent as application/x-www-form-urlencoded (what HTML forms and URLSearchParams produce), a space becomes +. Both decode back to a space in a query string, but + in a path is a literal plus sign.
Why do I get "URI malformed" when decoding?
decodeURIComponent throws when it finds a % that is not followed by two hex digits, or a sequence of bytes that is not valid UTF-8. A common cause is a literal % sign that was never encoded, such as "50% off". It should be sent as %25.