DEV Community

Snappy Tools
Snappy Tools

Posted on • Originally published at snappytools.app

URL Encoding Explained: Why %20 Means a Space (and When to Use encodeURIComponent)

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

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:

  • %20 is always safe in URLs
  • + works in query strings on servers that parse application/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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 %2520 instead of %20

Get the encoding right once and you'll never waste an afternoon debugging a mysteriously broken API call again.

Top comments (0)