encodeURI vs encodeURIComponent — the difference, and why 'Tom & Jerry' searches for 'Tom'

You're building search. The user types "Tom & Jerry", you build the link and carefully encode it:
const url = encodeURI("https://site.com/search?q=" + query);
// https://site.com/search?q=Tom%20&%20Jerry
The spaces became %20, looks tidy. But the server searches for just "Tom ". And it gets a weird second parameter named " Jerry".
Here's what happened: encodeURI left the & alone. And in a URL, & separates parameters. The link split in two. The function didn't break — you picked the wrong one.
The short answer
encodeURI— for a whole address. It encodes spaces, non-Latin letters and other "illegal" characters, but keeps the characters a URL is built from:: / ? # & = +and others.encodeURIComponent— for a piece inside the address: a parameter value, a path segment. It encodes almost everything, including& = + / ? #.
One sentence to remember: inserting a value — encode it as a component. Hardly anyone assembles whole addresses by hand.
An analogy: encodeURI is wrapping an assembled wardrobe without taking it apart. encodeURIComponent is wrapping every part separately so none falls out on the way.
The difference, point by point
- The
&character.encodeURIleaves it — and an&in the value cuts the parameter.encodeURIComponentturns it into%26, and the value arrives whole. - The
+character.encodeURI("C++")givesC++. But servers andURLSearchParamsread+in a query string as a space. A search for "C++" becomes "C" plus two spaces.encodeURIComponentgivesC%2B%2B— and it all gets through. - The
#character.encodeURIkeeps it — everything after#becomes the fragment and never reaches the server at all. The component version encodes it as%23. - The
/character. In a whole address you mustn't touch it; in a value you must. A file namereport/2026left unencoded turns into an extra folder. - Non-Latin letters and spaces. Both functions encode them the same way:
кот→%D0%BA%D0%BE%D1%82. - Double encoding. Both encode the
%sign. Encode something already encoded and%20becomes%2520. If you see%25in your logs — something is encoding twice. - A broken string. A lone half of an emoji (say, after cutting a string in the wrong place) crashes both functions with
URIError: URI malformed.
What to actually use
The honest answer: most of the time, neither by hand. Modern JavaScript has URL and URLSearchParams. They encode for you, and correctly:
const url = new URL("https://site.com/search");
url.searchParams.set("q", "Tom & Jerry C++");
url.href; // https://site.com/search?q=Tom+%26+Jerry+C%2B%2B
Notice: the space became + here, and the real plus became %2B. That's the form format, and servers understand it. You can't mix them up anymore.
Reading is just as easy: url.searchParams.get("q") returns "Tom & Jerry C++" with no manual decoding. The same approach powers the guide on keeping filters in the page URL.
How to catch this bug in a minute
Encoding bugs are sneaky: ordinary words work fine, only rare characters break. So test on purpose.
- Type a trap string into your search box:
Tom & Jerry C++ #1 50%. It has every dangerous character at once. - Open DevTools → Network and pick the request that went out.
- In the Payload tab (or Query String Parameters), look at what actually arrived. There should be one
qparameter holding the whole string.
See an extra parameter — the & wasn't encoded. See "C" with no pluses — + let you down. The string stops at #1 — the rest went into the fragment. And %2525 in the address means you're encoding twice.
Which one fits whom
- Building a link with parameters →
URL+searchParams.set. No contest. - Putting a value into a path (
/users/${name}) →encodeURIComponent(name). - You got a ready-made address with spaces or non-Latin letters and need to make it valid →
encodeURI. That's its only honest use case. And only if the address isn't encoded already. - The old
escape()function → leave it alone. It's deprecated and encodes non-Latin letters in a format servers don't understand.
When in doubt, use encodeURIComponent. Encoding a value too much breaks nothing; encoding it too little does.
Why doesn't decodeURIComponent turn plus into a space?
Because + as a space is a rule of the form format, not of URLs in general. decodeURIComponent("C++") returns "C++", while URLSearchParams returns "C" with spaces. Read parameters with the same tool you encoded them with.
Do I need to encode parameters for fetch?
Yes, if you glue the string together by hand. fetch won't fix an & in a value — it sends what you built. Pass it a URL with searchParams, and the question is closed.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





