If you've ever built a URL with user input in it, you've hit this problem: spaces and special characters break everything. A URL like https://example.com/search?q=hello world is invalid — the space has to become %20. That transformation is URL encoding, and understanding when and how to do it correctly saves a lot of debugging time.
What Is URL Encoding?
URLs can only contain a defined set of safe characters: letters (A–Z, a–z), digits (0–9), and a handful of symbols (-, _, ., ~). Everything else — spaces, &, =, #, /, non-ASCII characters — must be encoded.
URL encoding replaces unsafe characters with a percent sign followed by two hex digits representing the character's ASCII (or UTF-8) value. So:
| Character | Encoded |
|---|---|
| space | %20 |
& |
%26 |
= |
%3D |
# |
%23 |
/ |
%2F |
é |
%C3%A9 |
The %C3%A9 for é shows why it's not just ASCII — modern URL encoding uses UTF-8, where non-ASCII characters can span multiple bytes, each byte getting its own %XX escape.
The Two Forms You'll Actually Use
1. encodeURIComponent() — Encode a value
This is what you use for query parameter values and path segments. It encodes everything except letters, digits, -, _, ., and ~.
const query = "coffee & donuts";
const url = `https://example.com/search?q=${encodeURIComponent(query)}`;
// → https://example.com/search?q=coffee%20%26%20donuts
Notice that & gets encoded to %26. That matters — an unencoded & in a query value would be parsed as a new parameter separator, breaking your URL.
2. encodeURI() — Encode a full URL
This function leaves URL-structural characters (/, :, ?, &, =, #) alone, because it assumes you're encoding an already-formed URL and don't want to break its structure.
const url = "https://example.com/path with spaces/page?key=value";
encodeURI(url);
// → https://example.com/path%20with%20spaces/page?key=value
Rule of thumb: encodeURIComponent for values, encodeURI for whole URLs.
The Decoding Side: decodeURIComponent()
When you read query parameters from a URL, they're already encoded. You need to decode them before use:
const params = new URLSearchParams(window.location.search);
const q = params.get("q"); // URLSearchParams decodes automatically
// Manual approach:
const raw = "%48%65%6C%6C%6F"; // "Hello"
console.log(decodeURIComponent(raw)); // → Hello
URLSearchParams handles decoding automatically, which is why it's worth using over manual string splitting.
Common Mistakes
Encoding the whole URL twice
// Wrong — double-encoding the already-encoded URL
fetch(encodeURIComponent("https://api.example.com/data?key=value"));
// Right — only encode the value part
const key = encodeURIComponent("my value with spaces");
fetch(`https://api.example.com/data?key=${key}`);
Double-encoding turns %20 into %2520 (because % → %25). The server decodes once and gets %20 literally, not a space.
Using + instead of %20
HTML forms encode spaces as + in application/x-www-form-urlencoded bodies. URL encoding uses %20. The two formats are not interchangeable in all contexts:
-
%20is always safe in URLs -
+works in query strings on servers that parseapplication/x-www-form-urlencoded, but not in path segments
When in doubt, use %20 (i.e., use encodeURIComponent).
Forgetting non-ASCII characters
If you're building a URL with user input that might include accented characters, emoji, or any non-Latin script, you must encode it. encodeURIComponent handles this correctly — it UTF-8 encodes the character first, then percent-encodes each byte.
encodeURIComponent("café"); // → "caf%C3%A9"
encodeURIComponent("日本語"); // → "%E6%97%A5%E6%9C%AC%E8%AA%9E"
encodeURIComponent("🚀"); // → "%F0%9F%9A%80"
A Real-World Example: Building a Search URL
Imagine you're building a "Search on Wikipedia" link from user input:
function wikiLink(query) {
return `https://en.wikipedia.org/wiki/Special:Search?search=${encodeURIComponent(query)}`;
}
wikiLink("C++ programming language");
// → https://en.wikipedia.org/wiki/Special:Search?search=C%2B%2B%20programming%20language
Notice + in C++ becomes %2B. Without encoding, the server would interpret + as a space.
When You Need to Encode/Decode Quickly
For one-off encoding and decoding during development — converting a value, checking what a percent-encoded string says, or generating a URL hash — a browser-based tool is faster than switching to a Node.js REPL. I use the URL Encoder/Decoder at SnappyTools for this. Paste in a value, get the encoded form, done. No signup, 100% client-side.
Summary
- URL encoding replaces unsafe characters with
%+ two hex digits -
encodeURIComponent()— encode a value (encodes&,=,/,+, etc.) -
encodeURI()— encode a complete URL (leaves structural characters intact) -
URLSearchParams— the cleanest way to build and parse query strings in modern JavaScript - Non-ASCII characters are UTF-8 encoded first, then percent-encoded byte by byte
- Never encode the same string twice — you'll get
%2520instead of%20
Get the encoding right once and you'll never waste an afternoon debugging a mysteriously broken API call again.
Top comments (0)