JavaScript provides multiple ways to encode URLs, each with different use cases. Choosing the wrong one leads to broken URLs or security issues.
Key Takeaways
- 1encodeURIComponent() for encoding parameter values
- 2encodeURI() for encoding full URLs (preserves structure)
- 3URL API is the safest choice for building URLs
- 4URLSearchParams handles query string encoding automatically
- 5Never use escape()—it's deprecated and broken for Unicode
"encodeURIComponent() escapes all characters except: A-Z a-z 0-9 - _ . ! ~ * ' ( )"
Function Comparison
JavaScript offers three main approaches to URL encoding, each designed for different scenarios. Choosing the wrong one is a common source of bugs. The table below summarizes when to use each option.
| Function | Encodes | Preserves | Use For |
|---|---|---|---|
| encodeURIComponent | All special chars | Nothing | Parameter values |
| encodeURI | Spaces, unicode | :/?#[]@!$&'()*+,;= | Full URLs |
| URL API | Automatic | Structure | Building URLs safely |
The key distinction is what each function preserves. encodeURIComponent() encodes almost everything, making it safe for parameter values. encodeURI() preserves URL structure characters, which is why it shouldn't be used for values. The URL API automatically determines the right encoding based on context.
encodeURIComponent()
This function is your workhorse for encoding individual values like query parameter values, path segments, and form data. It encodes every character that could interfere with URL parsing, leaving only 71 unreserved characters unencoded.
// Use for parameter values
const searchTerm = 'Tom & Jerry';
const url = 'https://example.com/search?q=' + encodeURIComponent(searchTerm);
// "https://example.com/search?q=Tom%20%26%20Jerry"
// It encodes structural characters
encodeURIComponent('?'); // "%3F"
encodeURIComponent('&'); // "%26"
encodeURIComponent('='); // "%3D"
encodeURIComponent('/'); // "%2F"
// Unicode works too
encodeURIComponent('café'); // "caf%C3%A9"
encodeURIComponent('日本語'); // "%E6%97%A5%E6%9C%AC%E8%AA%9E"Notice how encodeURIComponent() encodes all the structural characters: ?, &, =, and /. This makes it safe to include any user input in a URL without breaking the URL structure. Unicode characters are converted to their UTF-8 byte sequences, then each byte is percent-encoded.
While encodeURIComponent() works great for values, you need a different approach when encoding complete URLs that should keep their structure intact.
encodeURI()
Use encodeURI() when you have a complete URL with spaces or other invalid characters in the path, but you want to preserve the URL's structure. This function is intentionally conservative—it only encodes characters that are never valid in URLs, like spaces and certain Unicode characters.
// Use for encoding a complete URL
const url = 'https://example.com/path with spaces/file.txt';
encodeURI(url);
// "https://example.com/path%20with%20spaces/file.txt"
// Preserves structural characters
encodeURI('?'); // "?" (not encoded!)
encodeURI('&'); // "&" (not encoded!)
encodeURI('='); // "=" (not encoded!)
encodeURI('/'); // "/" (not encoded!)
// WARNING: Don't use for parameter values!
const value = 'a=b&c=d';
encodeURI(value); // "a=b&c=d" (broken! & and = not encoded)The warning at the end is crucial: encodeURI() leaves & and = unencoded because these are valid URL characters. If your data contains these characters and you use encodeURI(), you'll create a broken URL. This is the most common encoding mistake in JavaScript.
For most use cases, the modern URL API is a better choice than either encoding function. It handles encoding automatically based on context.
URL API (Recommended)
The URL API, available in all modern browsers and Node.js, is the safest way to build and manipulate URLs. It handles encoding contextually—encoding values when you set them, preserving structure where appropriate, and decoding automatically when you read values. This eliminates an entire class of encoding bugs.
// Building URLs safely
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'Tom & Jerry');
url.searchParams.set('page', '1');
console.log(url.href);
// "https://example.com/search?q=Tom+%26+Jerry&page=1"
// The API handles all encoding
url.searchParams.set('redirect', 'https://other.com?foo=bar');
// Properly encodes the nested URL
// Reading parameters (auto-decodes)
console.log(url.searchParams.get('q')); // "Tom & Jerry"
// Modifying paths
url.pathname = '/api/v2/users/123';
// Building from parts
const api = new URL('https://api.example.com');
api.pathname = '/v1/users';
api.searchParams.set('status', 'active');
api.searchParams.append('role', 'admin');
api.searchParams.append('role', 'editor'); // Can have multiple valuesThe URL API shines when building complex URLs. Notice how it handles the nested URL in redirect—the inner URL's special characters are properly encoded without you needing to think about it. The append() method lets you add multiple values for the same key, which is common for filters and tags.
When you only need to work with query strings (not full URLs), URLSearchParams provides a lighter-weight alternative.
URLSearchParams
URLSearchParams is perfect when you need to parse or build query strings without dealing with the full URL. It's commonly used for form handling, API requests, and extracting parameters from location.search. The API handles both encoding and decoding automatically.
// Parse existing query string
const params = new URLSearchParams('?q=hello&page=1');
params.get('q'); // "hello"
params.get('page'); // "1"
// Build query string
const params = new URLSearchParams();
params.set('search', 'Tom & Jerry');
params.set('filter', 'active');
params.toString(); // "search=Tom+%26+Jerry&filter=active"
// From object
const params = new URLSearchParams({
q: 'hello world',
page: '1',
sort: 'date'
});
// Iterate
for (const [key, value] of params) {
console.log(`${key}: ${value}`);
}URLSearchParams is iterable, making it easy to loop through all parameters. You can also convert it directly to an object with Object.fromEntries(params), though be aware this only keeps the last value for duplicate keys. For full duplicate handling, use the iteration approach.
Understanding decoding is just as important as encoding. Let's look at how to safely decode URL-encoded strings.
Decoding
Decoding functions reverse the encoding process, converting percent-encoded sequences back to their original characters. The URL API decodes automatically when you access properties, but sometimes you need to decode strings manually, especially when working with raw query strings.
// Decode component
decodeURIComponent('Tom%20%26%20Jerry'); // "Tom & Jerry"
// Decode URI
decodeURI('https://example.com/path%20name'); // "https://example.com/path name"
// URL API auto-decodes
const url = new URL('https://example.com/search?q=hello%20world');
url.searchParams.get('q'); // "hello world" (decoded)
// Handle malformed encoding
try {
decodeURIComponent('%E0%A4%A'); // Invalid UTF-8
} catch (e) {
console.log('Invalid encoding');
}Always wrap manual decoding in a try-catch block. Invalid percent sequences (like incomplete UTF-8 or non-hex characters after %) will throw a URIError. The URL API is more forgiving and handles many edge cases gracefully, which is another reason to prefer it over manual encoding/decoding.