DEV Community

kingfujing
kingfujing

Posted on • Originally published at devtoolshub-seven.vercel.app

URL Encoding Decoded: A Developer's Guide to encodeURI vs encodeURIComponent

Originally published on DevToolsHub β€” free, privacy-first online tools for developers.

Every developer has seen it: %20, %3A, %2F β€” URL encoding is everywhere. Whether you're building REST APIs, handling form submissions, parsing query strings, or debugging OAuth redirects, understanding URL encoding is essential.

πŸ”§ Try it yourself: Use the free URL Encoder / Decoder β€” supports both encodeURIComponent and encodeURI modes with instant bidirectional conversion. All processing happens in your browser β€” zero data leaves your machine.


What is URL Encoding?

URL encoding (also called percent-encoding) converts special characters into a format that can be safely transmitted in URLs. Only a limited set of characters are allowed unescaped in URLs: alphanumeric characters (A-Z, a-z, 0-9) and a few special characters like -, _, ., ~.

Everything else gets encoded as a % followed by two hexadecimal digits representing the character's UTF-8 byte value.

Character Encoded Why It's Encoded
Space %20 Not allowed in URLs
? %3F Starts query string
& %26 Separates query params
= %3D Key-value separator
# %23 Starts fragment identifier
/ %2F Path separator
@ %40 Mailto/auth separator
δΈ­ %E4%B8%AD Non-ASCII character

The Two JavaScript Encoding Functions

JavaScript provides two built-in URL encoding functions β€” and choosing the wrong one is one of the most common bugs in web development.

encodeURI() β€” For Full URLs

encodeURI("https://example.com/search?q=hello world&lang=en")
// Result: "https://example.com/search?q=hello%20world&lang=en"
Enter fullscreen mode Exit fullscreen mode

encodeURI() encodes special characters except those that have structural meaning in a URL (?, &, =, #, /). It assumes you're encoding a complete URL and preserves its structure.

Use when: you have a complete URL that contains characters needing encoding (like spaces in the path).

encodeURIComponent() β€” For Query Parameters

encodeURIComponent("hello world & more")
// Result: "hello%20world%20%26%20more"

encodeURIComponent("name=John & age=30")
// Result: "name%3DJohn%20%26%20age%3D30"
Enter fullscreen mode Exit fullscreen mode

encodeURIComponent() encodes everything β€” including ?, &, =, #, and /. This is what you want when building query strings.

Use when: building query parameters, encoding user input for URLs, or handling data that needs to be embedded in a URL.

πŸ”΄ The Classic Bug

// ❌ WRONG β€” using encodeURI for query params
const name = "John & Jane";
const url = `https://api.example.com?name=${encodeURI(name)}`;
// Result: "https://api.example.com?name=John%20&%20Jane"
//                            The "&" was NOT encoded! ← BROKEN QUERY

// βœ… CORRECT β€” using encodeURIComponent for query params
const url = `https://api.example.com?name=${encodeURIComponent(name)}`;
// Result: "https://api.example.com?name=John%20%26%20Jane"
//                            Everything is encoded βœ“
Enter fullscreen mode Exit fullscreen mode

The Golden Rule: When building URL query strings, always use encodeURIComponent() for each parameter value. Never pass raw user input into a URL.


The Complete Character Reference

Here's what the two encoding modes leave untouched:

Character Group encodeURI() encodeURIComponent()
A-Z, a-z, 0-9 βœ… Preserved βœ… Preserved
- _ . ~ βœ… Preserved βœ… Preserved
! ' ( ) * βœ… Preserved βœ… Preserved
? βœ… Preserved ❌ Encoded β†’ %3F
& βœ… Preserved ❌ Encoded β†’ %26
= βœ… Preserved ❌ Encoded β†’ %3D
# βœ… Preserved ❌ Encoded β†’ %23
/ βœ… Preserved ❌ Encoded β†’ %2F
Space ❌ Encoded β†’ %20 ❌ Encoded β†’ %20
@ ❌ Encoded β†’ %40 ❌ Encoded β†’ %40
: ❌ Encoded β†’ %3A ❌ Encoded β†’ %3A

Real-World Scenarios

1. Building a Search URL

const baseUrl = "https://api.books.com/search";
const query = "JavaScript & CSS";
const category = "web dev";
const page = 1;

// βœ… Correct approach
const params = new URLSearchParams({
  q: query,
  cat: category,
  page: page.toString()
});

const url = `${baseUrl}?${params.toString()}`;
// "https://api.books.com/search?q=JavaScript+%26+CSS&cat=web+dev&page=1"
Enter fullscreen mode Exit fullscreen mode

Pro tip: Use URLSearchParams instead of manual string concatenation β€” it handles encoding correctly for all browsers.

2. Decoding URLs from Server Logs

Raw log entry:
  GET /search?q=JavaScript+%26+CSS&referrer=https%3A%2F%2Fgoogle.com

After decoding:
  Path: /search
  q: JavaScript & CSS
  referrer: https://google.com
Enter fullscreen mode Exit fullscreen mode

3. Handling Unicode and Emoji

encodeURIComponent("δ½ ε₯½δΈ–η•Œ")
// Result: "%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C"

encodeURIComponent("πŸ”₯ πŸš€")
// Result: "%F0%9F%94%A5%20%F0%9F%9A%80"

// βœ… Decoding is symmetrical
decodeURIComponent("%E4%BD%A0%E5%A5%BD")
// Result: "δ½ ε₯½"
Enter fullscreen mode Exit fullscreen mode

4. OAuth Redirect URLs

OAuth flows often require encoding the redirect_uri parameter:

const redirectUri = "https://myapp.com/callback?source=email&plan=premium";
const encoded = encodeURIComponent(redirectUri);
// "https%3A%2F%2Fmyapp.com%2Fcallback%3Fsource%3Demail%26plan%3Dpremium"

const oauthUrl = `https://auth.provider.com/authorize?redirect_uri=${encoded}&client_id=abc123`;
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls

Pitfall 1: Double Encoding

const input = "hello%20world";

// πŸ”΄ Double encoding β€” the %25 will be treated as a literal percent
const bad = encodeURIComponent(input);
// Result: "hello%2520world"
//                 ^^ This is "%25" followed by "20", NOT a space!

// βœ… Correct β€” only encode once
const good = input; // Already encoded, or use decodeURIComponent first
Enter fullscreen mode Exit fullscreen mode

Pitfall 2: Forgetting to Decode on the Backend

Most web frameworks handle URL decoding automatically (Express, Flask, Spring Boot all do). But if you're building a custom URL parser, remember to call decodeURIComponent().

Pitfall 3: The + vs %20 Confusion

Application/x-www-form-urlencoded forms encode spaces as +. But URL encoding (percent-encoding) uses %20. JavaScript's encodeURIComponent() uses %20, not +. URLSearchParams uses +. Both work β€” just be consistent.


Language Quick Reference

Language Encode Decode
JavaScript encodeURIComponent(str) decodeURIComponent(str)
Python urllib.parse.quote(str) urllib.parse.unquote(str)
Python (query) urllib.parse.urlencode(dict) urllib.parse.parse_qs(str)
Java URLEncoder.encode(str, "UTF-8") URLDecoder.decode(str, "UTF-8")
Go url.QueryEscape(str) url.QueryUnescape(str)
PHP urlencode($str) urldecode($str)
Ruby ERB::Util.url_encode(str) ERB::Util.url_decode(str)
C# Uri.EscapeDataString(str) Uri.UnescapeDataString(str)
Rust urlencoding::encode(str) urlencoding::decode(str)

URL Encoding Cheat Sheet

Quick Decision Tree

You have a complete URL?       β†’ use encodeURI()
You have a query param value?  β†’ use encodeURIComponent()
You're debugging encoded data? β†’ use decodeURIComponent()
Building query strings?        β†’ use URLSearchParams() βœ“
Enter fullscreen mode Exit fullscreen mode

Characters That Must Always Be Encoded in Query Params

<space>  β†’  %20    # hash    β†’  %23
&        β†’  %26    % percent β†’ %25
?        β†’  %3F    / slash  β†’  %2F
=        β†’  %3D    @ at     β†’  %40
Enter fullscreen mode Exit fullscreen mode

TL;DR β€” Just Remember This

  1. encodeURIComponent() for query parameter values (encodes everything)
  2. encodeURI() only if you need to encode a complete URL string
  3. Use URLSearchParams β€” it handles both encoding and parameter formatting
  4. All encoding/decoding is local β€” no server processing required

πŸ›  Need a quick encode/decode? Try the URL Encoder / Decoder tool β€” built in the browser, safe with your sensitive data, supports both encoding modes. Also check out JSON Formatter and JWT Decoder for more handy dev tools.


Found this helpful? Follow me for more developer guides and practical web development tips. If you spot an error or have a suggestion, drop a comment below!

Top comments (0)