encodeURI vs encodeURIComponent: When to Use Each
encodeURI vs encodeURIComponent explained — which reserved characters each one escapes, and why using the wrong one silently breaks query strings.
Try it now: URL Encoder & Decoder — Percent-encode and decode URLs and query strings, with explicit component, full-URL and form modes. Runs entirely in your browser.
They Encode the Same Characters, With One Critical Exception
encodeURI() and encodeURIComponent()both percent-encode "unsafe" characters in a string, and for most of the alphabet they agree completely: letters, digits, and the marks - _ . ! ~ * ' ( ) are left untouched by both. The entire difference between them — and the entire reason two functions exist instead of one — comes down to how each one treats a small set of characters that are structurally meaningful inside a URI: ; , / ? : @ & = + $ #.
encodeURI() assumes you're handing it a complete URI that may already contain those characters doing their job — a / separating path segments, a ? starting the query string, an & separating query parameters, a :after a scheme. It leaves all of them alone, because encoding them would corrupt the URI's own structure. Turn the / in /search?q=cats into %2F and you no longer have a path — you have a single opaque segment.
encodeURIComponent() assumes the opposite: that you're handing it a single pieceof a URI — a query parameter's value, one path segment, a fragment — that is about to be inserted into a larger URI you build yourself. For that use case, those same reserved characters are exactly what you need encoded. If the actual data in your component happens to contain a & or a /, and it isn't escaped, it stops being data and starts being read as URI structure — a second parameter that was never supposed to exist, or a path boundary that was never supposed to be there.
That's the whole distinction. Everything else — including the fact that neither one uses + for spaces, both use %20 — follows from it.
A Query Value With an Ampersand and a Slash in It
Here's the failure in practice. Say you're building a search URL and the user typed cats & dogs / cute into the search box. Run the entire target URL through encodeURI():
const value = "cats & dogs / cute";
const url = `https://example.com/search?q=${value}`;
encodeURI(url)
// → "https://example.com/search?q=cats%20&%20dogs%20/%20cute"Spaces became %20, but the & and / that belong to the search text were left alone — because encodeURI()has no way to know they're part of the value rather than part of the URI. A server parsing that query string now sees a parameter q with the value cats , followed by a second, meaningless parameter dogs / cute. The search silently breaks.
The fix is to encode only the value, before it goes anywhere near the template string:
const value = "cats & dogs / cute";
const encoded = encodeURIComponent(value);
// → "cats%20%26%20dogs%20%2F%20cute"
const url = `https://example.com/search?q=${encoded}`;
// → "https://example.com/search?q=cats%20%26%20dogs%20%2F%20cute"Now the & and / are escaped as %26 and %2F, so a server decodes exactly one parameter, q, with the exact value the user typed. This is the pattern to reach for by default whenever you're inserting a variable into a URL you're constructing: encode the variable with encodeURIComponent() first, then build the template string around the already-encoded result.
encodeURI vs encodeURIComponent, Side by Side
| Axis | encodeURI() | encodeURIComponent() |
|---|---|---|
| Intended input | A complete, already-structured URI | A single component: a query value, a path segment, a fragment |
Encodes ; , / ? : @ & = + $ #? | No — left untouched, since they mark URI structure | Yes — escaped, since they might be literal data |
| Encodes spaces as | %20 | %20 (neither ever produces +) |
Leaves ! ' ( ) * untouched? | Yes | Yes — a common surprise, since these aren't alphanumeric but neither function escapes them |
| Typical use case | Encoding a full URL right before fetch() or navigation | Encoding one value before inserting it into a URL you're building |
| Risk if used wrong | Rarely used at all in practice; using it on a single value under-encodes it and leaves reserved characters live | Using it on an entire URL over-encodes the structural characters — %3A%2F%2F instead of :// — and breaks the URI outright |
The ! ' ( ) *row trips people up the most. Both functions treat those four characters as safe and never escape them, even though they aren't alphanumeric and aren't in the conventional -_.~"unreserved" set either. If you're expecting fully escaped output — say, to match a strict allowlist on a server — encodeURIComponent()alone won't get you there; you'd need to post-process those characters yourself.
This Isn't Just a JavaScript Quirk
encodeURI and encodeURIComponentare JavaScript-specific function names, but the distinction they encode — encode a whole URI versus encode one value that's about to be inserted into one — shows up under different names in essentially every language's standard library. Python's urllib.parse module has quote()for components and leaves you to assemble full URLs yourself; other ecosystems draw the same line with their own naming. The underlying rule is universal: if you're encoding something that will become part of a larger URI, encode it as a component, not as a whole URI. General background on percent-encoding itself — what gets turned into %XX and why — is covered in the URL encoding guide.
Which One Should You Actually Use?
Two questions settle it every time:
- Are you about to
fetch()or navigate to a full URL, and it's already assembled? UseencodeURI()— it will leave the URL's own structural characters (/,?,&,:) alone and only encode genuinely unsafe characters like spaces or non-ASCII text that snuck into the string. - Are you inserting one value — a query parameter, a path segment, anything that came from user input or a variable — into a URL you're building? Use
encodeURIComponent(). This is, by a wide margin, the more common case in real code: search boxes, filter values, redirect URLs passed as a query parameter, IDs in a dynamic route. Encode the value first, then interpolate it into the template.
If you're not sure which situation you're in, default to encodeURIComponent()on the individual pieces and build the URL by concatenating already-encoded parts. It's very rare to need encodeURI() at all — the main legitimate case is encoding a user-supplied URL, spaces and all, right before passing it to fetch() or setting window.location, where the URL's own / and ? and & need to stay exactly where they are.
To try both directions against real input without opening a console, GenKitLab's URL Encoder & Decoder has explicit component, full-URL and form modes, so you can see exactly what each one does to a given string side by side — entirely in your browser, nothing sent anywhere.
Frequently asked questions
›What is the actual difference between encodeURI and encodeURIComponent?
encodeURI() encodes a complete URI, so it leaves URI-structural characters — ; , / ? : @ & = + $ # — untouched, since they're expected to already be doing their job. encodeURIComponent() encodes a single piece of a URI (a query value, a path segment), so it does escape those same characters, in case the actual data contains one of them.
›When should I use encodeURI instead of encodeURIComponent?
Almost only when you already have a complete, structurally valid URL and just need to escape things like spaces or non-ASCII characters in it — for example, before passing a user-supplied URL to fetch() or window.location. If you're inserting a single value into a URL you're constructing, use encodeURIComponent() instead; that's the far more common case.
›Does encodeURIComponent escape every special character?
No. It leaves alphanumerics untouched along with - _ . ! ~ * ' ( ) — that last group of four, ! ' ( ) *, is a common surprise, since people expect fully escaped output and don't get it. If you need those characters escaped too, you have to post-process the result yourself.
›Why does my query string break when I use encodeURI on a full URL with a search value?
Because encodeURI() deliberately does not encode &, /, =, or the other reserved characters — it assumes they're already part of the URL's structure. If your query value itself contains one of those characters, it gets read as a second parameter or a path boundary instead of as data. Encode the value alone with encodeURIComponent() before inserting it into the URL.
›Do encodeURI and encodeURIComponent encode spaces as + or %20?
Both encode a space as %20. Neither ever produces a +. The + convention for spaces comes from a different, older encoding used specifically for HTML form submissions (application/x-www-form-urlencoded), not from either of these JavaScript functions.
›Is this encodeURI vs encodeURIComponent distinction specific to JavaScript?
The function names are JavaScript-specific, but the underlying distinction — encode a whole URI versus encode one component that's about to be inserted into one — exists in every language's URL-encoding tools under different names, like Python's urllib.parse.quote() for components.
›What happens if I use encodeURIComponent on an entire URL by mistake?
It over-encodes the URL: the :// after the scheme becomes %3A%2F%2F, every / in the path becomes %2F, and every query separator gets escaped too. The result is no longer a valid, navigable URL — it's a single opaque string, which is exactly what you want for one component but breaks a full URL outright.
Last updated