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.

Frequently asked

Common questions.

Use encodeURIComponent for a single value — a query parameter, path segment, or fragment — because it escapes structural characters like ?, &, and /. Use encodeURI only for a complete, already-structured URL where you want those structural characters preserved. In most cases you want encodeURIComponent.

No. escape() is deprecated and produces non-standard %uXXXX sequences for non-ASCII characters that don’t conform to RFC 3986. Use encodeURIComponent for values and URLSearchParams for query strings instead.

Use URLSearchParams. It encodes each value exactly once as you add it and joins them correctly: new URLSearchParams({q: 'a b'}).toString() gives q=a+b. Don’t also wrap the values in encodeURIComponent — that double-encodes them.

You’re encoding something that was already encoded — commonly calling encodeURIComponent on a value and also passing it through URLSearchParams. The symptom is %2520 where you expected %20. Encode once, at the point you build the URL.

Sources & standards

Honest scope: MDN documents the JavaScript encoders; RFC 3986 defines the underlying percent-encoding. URLSearchParams uses form-encoding (+).

More reading

From the blog.

About this page

Written and maintained by the urlencodedecode.com team. Every technical claim on this page is verified against primary sources — the RFCs (3986, 3629, 4648, 7578), the WHATWG URL Standard, and official vendor or language documentation — rather than second-hand summaries. When a source contradicts a common assumption, we follow the source and note the discrepancy. Corrections: contactus@urlencodedecode.com.