Back to blog

URL Encoding: encodeURI vs encodeURIComponent

JavaScript has two URL encoding functions, and using the wrong one causes subtle bugs. Here's the difference, with examples and a decision tree.

Published September 3, 2026· by TextOpal Team· 4 min read
urlencodingjavascript

JavaScript gives developers two functions for URL encoding: encodeURI and encodeURIComponent. They look similar, they sound similar, and they often produce identical output — but they encode different sets of characters, and using the wrong one silently breaks your URLs.

This is one of those JavaScript gotchas that everyone runs into eventually. Let's demystify it once and for all.

The 30-second version

The distinction matters because URL syntax has two layers: the URL structure itself (which uses :, /, ? as delimiters) and the data inside the URL (which must not contain those delimiters unescaped, lest they be misinterpreted).

When to use which

Use encodeURIComponent for query parameter values

const query = 'name=John Doe&email=foo@bar.com';
const url = `https://example.com/search?q=${encodeURIComponent(query)}`;
// → https://example.com/search?q=name%3DJohn%20Doe%26email%3Dfoo%40bar.com

Notice that & and = inside the query string are encoded to %26 and %3D. This is correct — they're data, not URL structure. If you used encodeURI here, the & would survive unencoded, and the receiving server would interpret email=foo@bar.com as a separate query parameter.

Use encodeURI for complete URLs that contain non-ASCII characters

const url = 'https://example.com/path with spaces/файл.html';
const encoded = encodeURI(url);
// → https://example.com/path%20with%20spaces/%D1%84%D0%B0%D0%B9%D0%BB.html

The / and : are preserved because they're part of the URL structure. The spaces and Cyrillic characters are encoded because they're not valid in URLs.

Mix both for paths with dynamic segments

const baseUrl = 'https://example.com';
const segment = 'café résumé.pdf';
const fullUrl = `${baseUrl}/files/${encodeURIComponent(segment)}`;
// → https://example.com/files/caf%C3%A9%20r%C3%A9sum%C3%A9.pdf

Here you use encodeURIComponent on the segment because it's data inside the path — the / in café/résumé.pdf would otherwise be interpreted as a path separator.

Common mistakes

1. Using encodeURI for query parameter values.

// WRONG
const url = `?name=${encodeURI('foo&bar')}`;
// → ?name=foo&bar
// Server sees: name=foo, bar= (empty)

The & survives, and the server parses it as a parameter separator. Use encodeURIComponent instead.

2. Double-encoding.

// WRONG
const url = `?q=${encodeURIComponent(encodeURI('hello world'))}`;
// → ?q=hello%2520world

The first encoding produces hello%20world. The second encoding escapes the % to %25, producing hello%2520world. The server decodes once and gets hello%20world — not what you wanted.

3. Forgetting to encode UTM parameters.

UTM tags often contain spaces (e.g., utm_campaign=spring sale). If you don't encode them, the URL breaks at the first space. Worse, analytics tools may attribute traffic to the wrong campaign because the unencoded URL is split incorrectly.

4. Encoding already-encoded URLs.

If you pull a URL from the address bar and pass it through encodeURI again, the existing %20 becomes %2520. Always check whether the URL is already encoded before encoding it.

The unreserved set

RFC 3986 defines a small set of "unreserved characters" that have no special meaning and never need encoding:

Everything else can technically appear in a URL, but most other characters have either structural meaning (:, /, ?) or must be encoded (spaces, non-ASCII, control characters).

encodeURIComponent encodes everything except the unreserved set plus a few "sub-delimiters" (!, ', (, ), *). This is more aggressive than strictly required by RFC 3986, but it produces URLs that work universally.

Encoding non-ASCII characters

Both encodeURI and encodeURIComponent encode non-ASCII characters as UTF-8 byte sequences, with each byte as %XX. A Cyrillic character like й becomes %D0%B9 (two bytes). An emoji like 🚀 becomes %F0%9F%9A%80 (four bytes).

This is correct behavior — modern browsers and servers expect UTF-8 in URLs. Older servers that use Latin-1 will mangle these characters, but that's their problem.

The full URL building pattern

For most URL construction tasks, the right approach is:

function buildUrl(baseUrl, params) {
  const query = Object.entries(params)
    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
    .join('&');
  return `${baseUrl}?${query}`;
}

buildUrl('https://example.com/search', {
  q: 'café résumé',
  lang: 'fr-FR',
  page: 2,
});
// → https://example.com/search?q=caf%C3%A9%20r%C3%A9sum%C3%A9&lang=fr-FR&page=2

This handles edge cases like numeric values (via String(v)), empty parameters, and special characters consistently. Use URLSearchParams if you're targeting modern browsers — it handles encoding automatically.

What about URLSearchParams?

The modern URLSearchParams API (available in all modern browsers and Node 10+) handles encoding for you:

const params = new URLSearchParams({ q: 'café résumé', lang: 'fr-FR' });
const url = `https://example.com/search?${params.toString()}`;
// → https://example.com/search?q=caf%C3%A9+r%C3%A9sum%C3%A9&lang=fr-FR

Note: URLSearchParams uses + for spaces (form-encoding), not %20. Both are valid in query strings, but %20 is more universally correct in URLs.

The decision tree

  1. Are you encoding a complete URL? → encodeURI
  2. Are you encoding a single value inside a URL? → encodeURIComponent
  3. Building a URL from scratch? → URLSearchParams or a helper function

That's it. If you remember nothing else, remember that encodeURIComponent is the safer default — it over-encodes structural characters, but that's almost always better than under-encoding data characters.

TextOpal's URL Encoder tool handles both modes side-by-side, so you can see the difference instantly and copy the right one. Everything runs locally — your URLs and UTM parameters never leave your browser.

Try the tool

URL Encoder and Decoder

Related articles

TextOpal home