You paste a link into a chat message. It looks fine. Someone clicks it and lands on a search page for the literal text "hello world" instead of your results. Or the reverse: your API call works in the browser address bar but fails the moment a value contains an ampersand.
Nine times out of ten, the bug is one function name. JavaScript gives you two encoders that sound interchangeable and are not: encodeURIComponent and encodeURI. Picking the wrong one is the most common URL bug I see in frontend code, and it is a one-line fix once you know which job each one does.
The rule in one sentence
Encode one value with encodeURIComponent. Encode a whole URL with encodeURI. That is the entire rule. Everything below is just why.
What each one actually escapes
A URL has structural characters: : / ? & = #. They are the punctuation of the address. encodeURIComponent escapes all of them, because its job is to make a value that cannot break the structure around it. encodeURI leaves them alone, because its job is to keep a complete address clickable.
Try it on the same input, a search URL with a space in the query:
const url = "https://example.com/search?q=hello world&lang=en";
encodeURI(url);
// "https://example.com/search?q=hello%20world&lang=en"
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world%26lang%3Den"
The first result is still a working link. The second is not a link at all. It is a value, and as a value it is perfect: paste it after ?redirect= in another URL and it rides along without breaking anything.
The two mistakes everyone makes
Mistake one: encoding a whole URL with encodeURIComponent and then wondering why the link is dead. You see this in share buttons and redirect builders. The fix is encodeURI.
Mistake two: building a query string by hand with encodeURI on each value, and then watching an & inside a search term split your query in two. A user searches for "fish & chips" and your URL becomes ?q=fish & chips, which the server reads as q=fish plus a mystery parameter called " chips". The fix is encodeURIComponent on the value:
const q = "fish & chips";
const url = `https://example.com/search?q=${encodeURIComponent(q)}`;
// "https://example.com/search?q=fish%20%26%20chips"
If you are building query strings often, skip the template literal entirely. URLSearchParams does the encoding for you, with one quirk to know: it writes spaces as +, which is correct for form data but not the same as %20.
The two traps after the main rule
Spaces have two spellings. %20 is standard percent-encoding and safe everywhere. + means a space only in HTML form data. A literal plus sign must arrive as %2B, or it silently becomes a space. If your decoded text has mystery spaces where plus signs should be, this is why.
And if a decoded string still shows %20 or %25, it was encoded twice. Some step ran an encoder over an already-encoded value, turning %20 into %2520. Decode again to resolve it, then fix the step that doubled it. Double encoding is almost always a pipeline bug, not a data bug.
Try it yourself
I built a small free tool for exactly this debugging loop: it encodes and decodes both ways, flags double-encoded input, breaks a pasted URL into its parts, and lists its query parameters so you can see what a long redirect or tracking link actually carries. You can play with the examples from this post here: URL Encoder and Decoder. Everything runs in your browser, nothing is uploaded.
The rule is worth taping above your monitor: one value, encodeURIComponent. One whole URL, encodeURI. Get that right and an entire class of "the link works for me" bugs disappears.
Top comments (0)