What is URL encoding?
A URL is allowed to contain only unreserved characters (letters, digits, -, _, ., ~) and a defined set of reserved characters that carry structural meaning (/ ? # [ ] @ : ! $ & ' ( ) * + , ; =). Everything else must be percent-encoded.
Percent-encoding replaces a character with a % sign followed by two hexadecimal digits representing its UTF-8 byte value. A space (byte 0x20) becomes %20. The letter "é" (UTF-8 bytes 0xC3 0xA9) becomes %C3%A9.
Reserved characters must also be encoded when they appear as data values rather than URL structure. An ampersand in a query param value must be encoded as %26, otherwise the parser reads it as a param separator.
Common encodings reference
| Character | Encoded | Note |
|---|---|---|
| Space | %20 | Also written as + in form data |
| & | %26 | Separates query params : must be encoded in values |
| = | %3D | Separates key from value : must be encoded in values |
| / | %2F | Path separator : encode when used as data |
| ? | %3F | Starts query string : encode when used as data |
| # | %23 | Fragment identifier : encode when used as data |
| + | %2B | Means space in form encoding : encode when literal + is intended |
| @ | %40 | Used in email addresses and credentials |
| % | %25 | The encoding character itself : must always be encoded |
encodeURI vs encodeURIComponent
JavaScript gives you two built-in encoding functions, and choosing the wrong one is the most common URL encoding bug:
encodeURI()
Encodes everything except: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #
Leaves reserved URL chars alone. Safe for encoding a complete URL.
encodeURI("https://example.com/search?q=hello world") → "https://example.com/search?q=hello%20world"encodeURIComponent()
Encodes everything except: A-Z a-z 0-9 - _ . ! ~ * ' ( )
Encodes reserved chars like / ? & =. Safe for encoding a single query param value.
encodeURIComponent("hello & world") → "hello%20%26%20world"The common mistake: using encodeURI() on a query parameter value. It will leave & and = unencoded, breaking the query string. Always use encodeURIComponent() for individual values.
Form encoding: application/x-www-form-urlencoded
HTML forms (and many older APIs) use a slightly different encoding called application/x-www-form-urlencoded. It is like percent-encoding but with one difference: spaces are encoded as + instead of %20. Key-value pairs are separated by &, and keys and values are separated by =.
name=Alice+Smith&message=Hello+World%21&age=30
When you see a + in a URL query string, check whether you are dealing with form-encoded data. If you are, decode it as a space. If not, it is a literal plus sign.
Unicode characters
Characters outside ASCII must be UTF-8 encoded first, then each byte is percent-encoded. The letter "é" (U+00E9) has the UTF-8 byte sequence 0xC3 0xA9, so it becomes %C3%A9 in a URL.
encodeURIComponent() handles this automatically in JavaScript. You do not need to manually convert to UTF-8 before encoding.
Worked example: building a query string correctly
Suppose your search input is "coffee & tea" and you want to sort by "price/asc". Both values contain reserved characters that must be encoded before they go into the URL:
const query = 'coffee & tea'
const sort = 'price/asc'
const url = `https://example.com/search?q=${encodeURIComponent(query)}&sort=${encodeURIComponent(sort)}`
// https://example.com/search?q=coffee%20%26%20tea&sort=price%2Fasc The & and / inside the values are safely encoded so the URL parser does not misread them as structural characters. The & separating the two params is left raw because it is structural.
Frequently asked questions
Why does + sometimes mean a space?
In the application/x-www-form-urlencoded format (used by HTML forms and many older APIs), + is an alternative encoding for a space character. In standard percent-encoding (RFC 3986), + is a literal plus sign and space is always %20. When you see + in a query string, the server decodes it as a space only if it is parsing form-encoded data.
When should I use encodeURIComponent vs encodeURI?
Use encodeURIComponent when encoding a single value that will be placed inside a URL: a query parameter value, a path segment, or a hash fragment. Use encodeURI only when you have a full URL that is already correctly structured and you just need to escape unsafe characters without breaking its structure. In practice, encodeURIComponent is the one you will use most.
Is %20 or + correct for spaces in URLs?
Both are valid but in different contexts. %20 is the correct percent-encoding per RFC 3986 and works everywhere. + is only valid as a space in the query string portion of application/x-www-form-urlencoded data. For maximum compatibility, especially in API query parameters, prefer %20.
How do I decode a URL-encoded string in JavaScript?
Use decodeURIComponent() to decode a single encoded component: decodeURIComponent("hello%20world") returns "hello world". Use decodeURI() if you encoded a full URL with encodeURI(). The browser also exposes the URL API (new URL(str)) which parses and decodes a complete URL into its components.
What happens with double encoding?
Double encoding occurs when you encode an already-encoded string. The % in %20 itself gets encoded to %2520, so the server receives the literal text "%20" rather than a space. This is a common bug when building URLs by concatenating strings. Always encode raw values, never pre-encoded ones.