Skip to content
DDevToolery

30 April 2025 · 5 min read

encodeURI or encodeURIComponent?

Two functions, one letter apart, with opposite jobs. Picking wrong breaks a URL or silently changes what it means.

Percent-encoding exists because a URL has structure. Certain characters mean something — a slash separates path segments, a question mark starts the query, an ampersand separates parameters. When a value contains one of those characters, it must be escaped or it will be read as structure.

The difference

encodeURI assumes you are handing it a complete URL and preserves the characters that give it structure. encodeURIComponent assumes you are handing it a single value and escapes everything that could be mistaken for structure.

const value = "a/b?c=d&e";

encodeURI(value);           // "a/b?c=d&e"      — unchanged
encodeURIComponent(value);  // "a%2Fb%3Fc%3Dd%26e"

The rule

Encoding one piece that is about to be inserted into a URL means encodeURIComponent. Encoding a whole URL that is already assembled means encodeURI. The second case is rarer than people think.

The failure is quiet. A search for “rock & roll” encoded with the wrong function becomes two query parameters, and the server receives “rock ” with the rest discarded.

Better than both

For query strings, do not concatenate at all. URLSearchParams handles the encoding and the joining, and cannot produce a malformed result:

const url = new URL("https://example.com/search");
url.searchParams.set("q", "rock & roll");
url.searchParams.set("page", "2");
url.toString();
// https://example.com/search?q=rock+%26+roll&page=2

The plus sign

Query strings have a second convention, inherited from HTML forms: a plus sign means a space. So %20 and + are both spaces in a query string, but only %20 is a space in a path. encodeURIComponent produces %20 always, which is safe everywhere; URLSearchParams produces + in queries, which is also correct there. Decoding is where it goes wrong — decodeURIComponent leaves a literal plus alone, so form-encoded values need the plus converted first.

What neither function does

  • Neither escapes for HTML. Putting a URL into an attribute still needs entity encoding, or you have an injection.
  • Neither validates. Both will happily encode nonsense into something that looks like a URL.
  • Neither handles internationalised domain names — hostnames use punycode, not percent-encoding.
  • Neither knows about your server's parsing quirks. Some frameworks decode twice, which is its own family of bugs.

Double encoding

Encoding an already-encoded value turns %20 into %2520. This usually happens when a URL is built in one place and encoded again in another. If you see %25 in a URL, something has been through the process twice — decode repeatedly until it stops changing to find the original.

Tools mentioned