Article · May 3, 2026 · 7 min read

URL encoding in JavaScript

JavaScript gives you four built-in functions for URL encoding — and they have different rules. Picking the wrong one is the source of most URL-encoding bugs in front-end and Node.js code. This article maps each function to the situations it’s designed for, with the gotchas spelled out.

The four built-ins

JavaScript’s URL-related global functions:

encodeURIComponent: the safe default

Use encodeURIComponent for individual values — query parameter values, path segments, hash fragments. It encodes everything that has any chance of being structurally meaningful.

encodeURIComponent('Hello, World!')
// → 'Hello%2C%20World!'

encodeURIComponent('https://example.com/path')
// → 'https%3A%2F%2Fexample.com%2Fpath'

Notice the second example encodes the colons and slashes — that’s what you want when embedding one URL inside another (e.g., as a redirect parameter).

encodeURI: only when you have a structural URL

Use encodeURI only when you have a complete URL where structural characters like :, /, ?, & are meant to stay literal. It doesn’t encode the reserved characters.

encodeURI('https://example.com/search?q=hello world')
// → 'https://example.com/search?q=hello%20world'

The space gets encoded, but the colon and slashes stay. The problem: in real code, the part of the URL that has data is rarely identical to the part you want to encode. If the user’s search query is “A/B testing,” encodeURI leaves the slash literal — which becomes a path separator and breaks your URL.

In practice, encodeURI is the wrong choice almost every time. Reach for encodeURIComponent by default; use encodeURI only when you understand exactly why you need to preserve reserved characters.

escape: never use

The global escape() function is from JavaScript’s prehistory. It uses non-standard %uXXXX for non-ASCII characters. The result isn’t a valid URL-encoded string — it’s a JavaScript-specific format that no other system understands.

escape('café')
// → 'caf%E9'    // wrong — should be UTF-8: '%C3%A9'

escape('☺')
// → '%u263A'    // not valid URL encoding

Both escape and unescape are marked deprecated in the JavaScript spec. The MDN docs explicitly recommend not using them.

URLSearchParams: the right way to build query strings

When you’re constructing a URL with parameters, don’t concatenate strings — use URLSearchParams. It handles all the encoding for you, correctly.

const params = new URLSearchParams();
params.append('q', 'Hello, World!');
params.append('lang', 'en');
params.append('tags', 'js');
params.append('tags', 'url');

params.toString();
// → 'q=Hello%2C+World%21&lang=en&tags=js&tags=url'

Notice how the comma and exclamation in the value got encoded, the space became + (form-encoded variant), and repeated keys (tags) were appended correctly. This is what an HTML form would produce.

To parse a query string the same way:

const params = new URLSearchParams(window.location.search);
params.get('q');           // 'Hello, World!' (auto-decoded)
params.getAll('tags');     // ['js', 'url']

URL: the modern URL parser

For working with whole URLs, the URL constructor is the right tool. It splits a URL into structured properties and handles all the encoding rules per WHATWG URL spec.

const u = new URL('https://example.com/path?q=hello#section');
u.protocol;       // 'https:'
u.host;           // 'example.com'
u.pathname;       // '/path'
u.search;         // '?q=hello'
u.hash;           // '#section'
u.searchParams;   // URLSearchParams object

You can also build URLs by mutating the properties:

const u = new URL('https://example.com');
u.pathname = '/products/My Item';   // space gets encoded
u.searchParams.set('lang', 'fr');
u.toString();
// → 'https://example.com/products/My%20Item?lang=fr'

Common pitfalls

Pitfall 1: Concatenating instead of using URLSearchParams

// Wrong — you have to remember to encode manually
const url = 'https://api.example.com/search?q=' + userInput;

// Right
const u = new URL('https://api.example.com/search');
u.searchParams.set('q', userInput);
const url = u.toString();

Pitfall 2: Double-encoding

// User input is already encoded once
const userInput = 'Hello%2C%20World';

// Wrong: encoding again
encodeURIComponent(userInput);
// → 'Hello%252C%2520World'   — the % got encoded as %25

If you receive input you suspect might already be encoded, decode once before re-encoding.

Pitfall 3: Using + for spaces in path components

URLSearchParams.toString() uses + for spaces because it produces form-encoded output — correct for query strings. But if you’re building a path component, you need %20:

// Wrong — the + means literal + in a path
path: '/users/John+Doe/profile'

// Right
path: '/users/' + encodeURIComponent('John Doe') + '/profile'
// → '/users/John%20Doe/profile'

Pitfall 4: Encoding the entire URL

// Wrong — this produces a broken URL
const fullUrl = encodeURIComponent('https://example.com/search?q=hello');
// → 'https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello'
// You’ve encoded the URL’s own structure, which means it’s no longer a URL

// Right — only encode parts that are data
const baseUrl = 'https://example.com/search';
const q = encodeURIComponent('hello, world');
const fullUrl = baseUrl + '?q=' + q;

Picking the right function

Situation Use
Building a query stringURLSearchParams
Parsing a URLnew URL()
Encoding a single value (query param, path segment, hash)encodeURIComponent
Decoding a single valuedecodeURIComponent
Encoding a whole URL with reserved chars intactencodeURI (rarely)

Default to URLSearchParams and new URL() when you can; they encode correctly automatically. Reach for encodeURIComponent when you have a value you specifically need to encode and the surrounding context doesn’t have its own helper. Avoid escape and encodeURI unless you have a specific reason.


Found this useful? Try the URL decoder or browse all tools.

More reading

From the blog.