If you've ever seen %20 in a URL and wondered what it means, or had your app crash because a user's name contained a & symbol, you're in the right place. URL encoding is one of those fundamentals that every web developer needs to understand — but few truly master.
In this guide, we'll break down how URL encoding works, when to use encodeURIComponent vs encodeURI, and how to handle edge cases that trip up even experienced developers.
What Is URL Encoding?
URLs can only contain a limited set of ASCII characters: letters, digits, and a few special characters like -, _, ., and ~. Anything outside this set — spaces, Chinese characters, emojis, even some punctuation — must be percent-encoded.
The encoding replaces each unsafe character with % followed by two hexadecimal digits representing the character's byte value in UTF-8.
For example:
- Space →
%20 -
&→%26 -
中→%E4%B8%AD -
🎉→%F0%9F%8E%89
JavaScript's Built-in Encoding Functions
JavaScript provides two main functions for URL encoding, and choosing the wrong one is a common source of bugs.
encodeURI
Use this when you want to encode a complete URL. It preserves characters that have special meaning in URLs, such as :, /, ?, #, &, and =.
const url = 'https://example.com/search?q=hello world&lang=zh';
const encoded = encodeURI(url);
// Result: https://example.com/search?q=hello%20world&lang=zh
Notice that ://, ?, and & are preserved — only the space was encoded.
encodeURIComponent
Use this when you want to encode a single component of a URL, like a query parameter value. This function encodes everything except A-Z, a-z, 0-9, -, _, ., !, ~, *, ', (, and ).
const param = 'hello world & goodbye';
const encoded = encodeURIComponent(param);
// Result: hello%20world%20%26%20goodbye
Here, the & is encoded to %26 — exactly what you want when building query strings.
The Golden Rule
Use
encodeURIComponentfor individual parameter values. UseencodeURIonly when encoding an entire URL that's already well-formed.
Building Query Strings Safely
Here's a pattern I use in production code:
function buildQueryString(params) {
return Object.entries(params)
.map(([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
)
.join('&');
}
const qs = buildQueryString({
search: 'JavaScript 教程',
page: 2,
filter: 'price < 100 & in stock'
});
// Result: search=JavaScript%20%E6%95%99%E7%A8%8B&page=2&filter=price%20%3C%20100%20%26%20in%20stock
This ensures every key and value is independently encoded, preventing injection issues.
Decoding URLs
JavaScript also provides two corresponding decode functions:
decodeURI
Decodes a string that was encoded with encodeURI. It won't decode characters like %2F (which represents /) because / is a valid URL character.
const encoded = 'https://example.com/search?q=hello%20world';
const decoded = decodeURI(encoded);
// Result: https://example.com/search?q=hello world
decodeURIComponent
Decodes a string that was encoded with encodeURIComponent. It will decode everything.
const encoded = 'hello%20world%20%26%20goodbye';
const decoded = decodeURIComponent(encoded);
// Result: hello world & goodbye
Common Pitfalls (and How to Avoid Them)
1. Double Encoding
This happens when you encode a value that's already been encoded:
const url = 'https://example.com/?q=hello%20world';
const bad = encodeURI(url);
// Result: https://example.com/?q=hello%2520world ← The % got encoded!
const good = encodeURI('https://example.com/?q=hello world');
// Result: https://example.com/?q=hello%20world ← Correct
Fix: Always encode raw values, never re-encode.
2. Mixing Up the Two Functions
// ❌ Wrong: encodeURI won't encode & so your parameter value gets split
const url = `https://api.example.com?name=${encodeURI('Tom & Jerry')}`;
// Result: https://api.example.com?name=Tom%20&%20Jerry ← The & breaks the query string!
// ✅ Correct: use encodeURIComponent for parameter values
const url = `https://api.example.com?name=${encodeURIComponent('Tom & Jerry')}`;
// Result: https://api.example.com?name=Tom%20%26%20Jerry ←
3. Forgetting About + for Spaces
In application/x-www-form-urlencoded (used in form POST data), spaces are encoded as + instead of %20. JavaScript's encodeURIComponent uses %20, which is valid in URLs but not in form data.
// For form data, replace %20 with +
const formData = encodeURIComponent('hello world').replace(/%20/g, '+');
// Result: hello+world
Or use URLSearchParams, which handles this automatically:
const params = new URLSearchParams({ name: 'hello world' });
params.toString();
// Result: name=hello+world
Modern Alternative: URLSearchParams
For most use cases, URLSearchParams is the cleanest approach:
const params = new URLSearchParams({
search: 'JavaScript 指南',
page: '1',
sort: 'desc'
});
const queryString = params.toString();
// Result: search=JavaScript+%E6%8C%87%E5%8D%97&page=1&sort=desc
const url = `https://example.com/search?${queryString}`;
Benefits:
- Automatically encodes keys and values
- Handles
+for spaces correctly - Provides methods like
.get(),.set(),.append(),.has() - Well-supported in all modern browsers and Node.js
Practical Example: Building an API Request
Let's put it all together with a real-world example:
async function searchProducts(query, filters) {
const params = new URLSearchParams({
q: query,
...filters
});
const response = await fetch(`https://api.example.com/products?${params}`);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
// Usage
searchProducts('无线耳机', {
min_price: '100',
max_price: '500',
category: '数码 & 电子' // The & is safely encoded
}).then(console.log);
Quick Reference Table
| Character | encodeURI |
encodeURIComponent |
|---|---|---|
| Space | %20 |
%20 |
& |
& |
%26 |
= |
= |
%3D |
? |
? |
%3F |
/ |
/ |
%2F |
# |
# |
%23 |
中 |
%E4%B8%AD |
%E4%B8%AD |
Summary
- Use
encodeURIComponentfor individual query parameter values - Use
encodeURIonly for complete URLs that are already well-formed - Prefer
URLSearchParamsfor building query strings in modern code - Never double-encode — always encode raw values
- For form POST data, spaces should be
+not%20
If you want to test URL encoding/decoding interactively, I've built a free online URL encoder/decoder tool that supports UTF-8 and handles all edge cases. It's 100% client-side — your data never leaves your browser.
Found this helpful? Follow me for more practical JavaScript guides. Questions? Drop a comment below!
Top comments (0)