encodeURIComponent vs encodeURI: URL Encoding Explained

encodeURI is for a whole URL, encodeURIComponent is for one value inside it. See what each escapes, why + becomes a space, and how to avoid double encoding.

JavaScript has two built-in functions for percent-encoding, and picking the wrong one is one of the most common URL bugs. encodeURI is for a whole URL. encodeURIComponent is for one piece of a URL, such as a single query value or path segment. Use the first on a value and its & or = breaks your query string. Use the second on a whole URL and the :// and / get escaped too.

You can try both modes below on your own input. Everything runs in your browser, so nothing you paste is uploaded.

URL Encoder & DecoderPercent-encode text for use in a URL, or decode it back. Choose whether you are encoding one query value or an entire URL, because the two need different escaping.

What percent-encoding does

A URL can only contain a limited set of ASCII characters, and some of them have a structural meaning: / separates path segments, ? starts the query, & separates parameters, = splits a key from its value and # starts the fragment. Any character that is not allowed, or that would be mistaken for one of these separators, is written as % followed by the hex value of each of its UTF-8 bytes.

space  →  %20
&      →  %26
é      →  %C3%A9      (two UTF-8 bytes)
☕     →  %E2%98%95   (three UTF-8 bytes)

The difference in one example

Take a URL with a search term that contains &:

const url = 'https://example.com/search?q=rock & roll&lang=en#top';

encodeURI(url);
// https://example.com/search?q=rock%20&%20roll&lang=en#top

encodeURIComponent(url);
// https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Drock%20%26%20roll%26lang%3Den%23top

encodeURI kept the URL usable but left the & inside "rock & roll" alone, so the server now sees a parameter called roll and the search term is just rock . encodeURIComponent escaped everything, including the :// and /, so the result is no longer a URL at all. Neither call produced the right answer, because the input mixed structure and data. The fix is to encode only the data:

const q = 'rock & roll';
const url = `https://example.com/search?q=${encodeURIComponent(q)}&lang=en`;
// https://example.com/search?q=rock%20%26%20roll&lang=en

Which characters each function leaves alone

CharactersencodeURIComponentencodeURI
A-Z a-z 0-9 - _ . ! ~ * ' ( )keptkept
; , / ? : @ & = + $ #escapedkept
Space, quotes, < > and non-ASCIIescapedescaped
%escaped to %25escaped to %25

The second row is the whole story. encodeURI assumes those characters are structure and leaves them in place; encodeURIComponent assumes they are data and escapes them.

Rule of thumb

  • Building a URL from parts: run encodeURIComponent on each value, never on the parts you typed yourself.
  • Cleaning up a complete URL that has spaces or non-ASCII in it: encodeURI is acceptable, but only if the URL's own data is already encoded correctly.
  • In modern code: let URL and URLSearchParams do it for you.
const u = new URL('https://example.com/search');
u.searchParams.set('q', 'rock & roll');
u.searchParams.set('next', '/a?b=1');
u.href;
// https://example.com/search?q=rock+%26+roll&next=%2Fa%3Fb%3D1

Why a space is sometimes +

URLSearchParams wrote the space as +, not %20. That comes from HTML form encoding (application/x-www-form-urlencoded), which is what browsers send when a form is submitted. In a query string both forms mean a space, but the decoders do not agree:

decodeURIComponent('a+b');               // "a+b"
new URLSearchParams('q=a+b').get('q');   // "a b"

So if you decode a form-style query string with decodeURIComponent, spaces come back as literal + signs. The reverse bug is worse: a real + in a value, such as a phone number like +62 812, must be sent as %2B, or a form decoder turns it into a space. encodeURIComponent and URLSearchParams both do this correctly; hand-built strings often do not.

Common errors

  1. Double encoding. Encoding a value that is already encoded turns % into %25, so a space shows up as %2520. Encode once, at the point where the value goes into the URL, and decode once where it comes out.
  2. URIError: URI malformed. decodeURIComponent('100%') throws, because a % must be followed by two hex digits. A stray % or a truncated multi-byte sequence like %E4%B8 both cause it. Wrap decoding in try/catch when the input is user supplied.
  3. Broken redirects. A next or redirect_uri parameter that holds another URL must be encoded with encodeURIComponent, or its own ? and & merge into the outer query string.
  4. Garbled non-English text. Percent-encoding works on UTF-8 bytes. Older systems that decode as Latin-1 turn %C3%A9 into é instead of é.
  5. Characters encodeURIComponent skips. It leaves ! ' ( ) * alone. They are legal in URLs, but a few strict APIs (some OAuth 1.0 signatures, for example) require them escaped as well.

Percent-encoding is not Base64URL

Both make data safe for URLs, but they solve different problems. Percent-encoding keeps readable text mostly readable and escapes only the characters that need it. Base64URL turns arbitrary bytes, including binary data, into a compact string that uses only URL-safe characters, at the cost of making it unreadable. If you are putting a token or binary blob into a URL, Base64URL is usually the better fit; for search terms, names and paths, use percent-encoding.

Base64 Encoder & DecoderEncode text to Base64 or decode it back. Unicode is handled correctly, so Japanese, Chinese, Korean and emoji all round-trip without corruption.

If the finished URL needs to be scanned from a phone or a printed page, you can turn it into a QR code once it is encoded correctly.

QR Code GeneratorGenerate a QR code for a URL, text or Wi-Fi network and download it as PNG or SVG. Everything happens in your browser, so nothing is uploaded and the code never expires.

Try the tools