Encodings / Guides

encodeURI vs encodeURIComponent

Both percent-encode characters that aren't safe in a URL. They differ on which characters they consider "safe" — and that difference is the source of a famous category of query-string bugs. Here's the rule, the table, and the fix.

Try the URL encoder →

The two-line answer

If you don't already know your input contains a complete URL, you want encodeURIComponent.

The character difference

The two functions agree on letters, digits, and the unreserved characters - _ . ~. They split on the URL syntax characters:

CharacterencodeURIencodeURIComponent
:kept%3A
/kept%2F
?kept%3F
#kept%23
&kept%26
=kept%3D
+keptkept ⚠️
$kept%24
,kept%2C
@kept%40
space%20%20
literal UnicodeUTF-8 percent-encodedUTF-8 percent-encoded

The + row is highlighted because it's a footgun on its own — see below.

The classic bug

You build a URL by string concatenation:

const q = "shoes & socks";
const url = "https://example.com/search?q=" + encodeURI(q);
// → https://example.com/search?q=shoes%20&%20socks

The & didn't get escaped. The receiving server sees the URL as having two query parameters: q=shoes and %20 (with no value). Your search breaks.

Fix:

const url = "https://example.com/search?q=" + encodeURIComponent(q);
// → https://example.com/search?q=shoes%20%26%20socks

Or, the canonical way:

const u = new URL("https://example.com/search");
u.searchParams.set("q", q);
url.toString();
// → https://example.com/search?q=shoes+%26+socks

Note that URLSearchParams encodes spaces as + (form-style), while encodeURIComponent uses %20. Both are valid; servers accept either when parsing queries.

The + / space gotcha

URLs have two standards for spaces:

The bug surfaces when a value containing a literal + is sent through encodeURIComponent (which doesn't escape +) and read by a server that treats + as a space:

const v = "1+1=2";
encodeURIComponent(v);                    // "1%2B1%3D2" — wait, no
                                           // actually it's "1%2B1%3D2"

OK, that one's fine — encodeURIComponent escapes =. But test on the destination server. Many frameworks (PHP's $_GET, Express's query parser) decode + as a space. If you stick a literal + into a URL via direct string concat, watch out.

The five percent-decoders, ordered

For completeness, JavaScript has these:

Decision flowchart

  1. Are you building a URL? → Use new URL(base) and its searchParams. Don't concatenate strings.
  2. Are you encoding one piece of an existing URL (a path segment, a single query value)? → encodeURIComponent.
  3. Are you encoding a complete URL whose pieces are already correct but contains some non-ASCII characters? → encodeURI. (Rare. Usually means a malformed URL was built upstream.)
  4. Are you using escape? → Stop. It's deprecated.

Reference

FAQ

Which one should I use 99% of the time?

encodeURIComponent. You almost never want to encode a whole URL — you want to encode a value going into a URL. The exception is constructing a full URL from a base, but even then it's safer to use the URL constructor and URLSearchParams.

Why does my '+' in a query string get parsed as a space on the server?

Form-style encoding (application/x-www-form-urlencoded) treats + as a space. encodeURIComponent does NOT escape +, so a literal + in your value will be misinterpreted by servers parsing form data. Workaround: encodeURIComponent(value).replace(/%20/g, '+').replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A') — or use URLSearchParams which gets this right.

Why does encodeURIComponent leave ! ' ( ) * unescaped?

Historical accident. RFC 3986 marks them as 'reserved' (sub-delims), but ECMA-262's encodeURIComponent predates that classification. They're safe in URL paths and query string values, but if you're embedding into form-encoded data you may need to escape them yourself.

Is decodeURIComponent the inverse of encodeURIComponent?

Yes, except decodeURIComponent throws a URIError on malformed percent-escapes (like %ZZ or a lone %). Wrap in try/catch when decoding untrusted input.

Should I use URL and URLSearchParams instead?

For non-trivial cases, yes. URLSearchParams handles the + / space mismatch correctly, escapes correctly for query strings, and rejects malformed inputs. Use new URL(base) and url.searchParams.append(k, v) and you'll dodge most encoding bugs entirely.

Open the URL encoder →