30 April 2025 · 5 min read
encodeURI or encodeURIComponent?
Two functions, one letter apart, with opposite jobs. Picking wrong breaks a URL or silently changes what it means.
Percent-encoding exists because a URL has structure. Certain characters mean something — a slash separates path segments, a question mark starts the query, an ampersand separates parameters. When a value contains one of those characters, it must be escaped or it will be read as structure.
The difference
encodeURI assumes you are handing it a complete URL and preserves the characters that give it structure. encodeURIComponent assumes you are handing it a single value and escapes everything that could be mistaken for structure.
const value = "a/b?c=d&e"; encodeURI(value); // "a/b?c=d&e" — unchanged encodeURIComponent(value); // "a%2Fb%3Fc%3Dd%26e"
The rule
Encoding one piece that is about to be inserted into a URL means encodeURIComponent. Encoding a whole URL that is already assembled means encodeURI. The second case is rarer than people think.
The failure is quiet. A search for “rock & roll” encoded with the wrong function becomes two query parameters, and the server receives “rock ” with the rest discarded.
Better than both
For query strings, do not concatenate at all. URLSearchParams handles the encoding and the joining, and cannot produce a malformed result:
const url = new URL("https://example.com/search");
url.searchParams.set("q", "rock & roll");
url.searchParams.set("page", "2");
url.toString();
// https://example.com/search?q=rock+%26+roll&page=2The plus sign
Query strings have a second convention, inherited from HTML forms: a plus sign means a space. So %20 and + are both spaces in a query string, but only %20 is a space in a path. encodeURIComponent produces %20 always, which is safe everywhere; URLSearchParams produces + in queries, which is also correct there. Decoding is where it goes wrong — decodeURIComponent leaves a literal plus alone, so form-encoded values need the plus converted first.
What neither function does
- Neither escapes for HTML. Putting a URL into an attribute still needs entity encoding, or you have an injection.
- Neither validates. Both will happily encode nonsense into something that looks like a URL.
- Neither handles internationalised domain names — hostnames use punycode, not percent-encoding.
- Neither knows about your server's parsing quirks. Some frameworks decode twice, which is its own family of bugs.
Double encoding
Encoding an already-encoded value turns %20 into %2520. This usually happens when a URL is built in one place and encoded again in another. If you see %25 in a URL, something has been through the process twice — decode repeatedly until it stops changing to find the original.
Tools mentioned