encodeURI vs encodeURIComponent: How to Encode Query Parameters Correctly
URL encoding bugs usually appear when a parameter contains &, =, ?, #, spaces, non-English text, or another URL. The most common JavaScript mistake is using encodeURI() when the value should be encoded with encodeURIComponent().
The short rule:
- use
encodeURI()for a complete URL - use
encodeURIComponent()for a parameter value or path segment
Why the difference matters
encodeURI() preserves characters that have meaning in a full URL, such as:
: / ? # & =
That is useful when you already have a complete URL and only need to escape spaces or non-ASCII characters.
encodeURIComponent() encodes more characters because it is meant for one component of a URL, such as a query parameter value.
If a parameter value contains &, =, or #, encodeURI() can break the query string.
Query parameter example
Suppose you want:
const search = 'a=b & c=d'
Wrong:
const url = '/search?q=' + encodeURI(search)
The & may be interpreted as a new query parameter separator.
Right:
const url = '/search?q=' + encodeURIComponent(search)
This keeps the whole value inside q.
Encoding a URL inside another URL
Redirect parameters are a common source of bugs.
const next = 'https://example.com/a?x=1&y=2'
const loginUrl = '/login?next=' + encodeURIComponent(next)
If you use encodeURI(next), the nested &y=2 can become part of the outer /login query string instead of staying inside next.
Path segment example
For a path segment, use encodeURIComponent():
const username = 'Ada Lovelace/notes'
const url = '/users/' + encodeURIComponent(username)
The slash is part of the username, not a path separator, so it must be encoded.
If you use encodeURI() here, the slash remains and changes the route structure.
Prefer URL and URLSearchParams
For query strings, modern JavaScript gives you safer APIs:
const url = new URL('https://example.com/search')
url.searchParams.set('q', 'a=b & c=d')
url.searchParams.set('lang', 'zh-CN')
console.log(url.toString())
URLSearchParams handles encoding and separators for you. It also reduces the chance of double encoding.
Full URL, query value, path segment, and hash
Many encoding bugs happen because the same string passes through several URL layers. Treat each layer separately:
| Situation | Safer choice | Why |
|---|---|---|
| Complete URL | encodeURI() | Keeps :, /, ?, &, and # meaningful |
| Query parameter value | encodeURIComponent() or URLSearchParams | Encodes &, =, ?, and # inside the value |
| Path segment | encodeURIComponent() | Encodes / so it does not become another route level |
| Hash value | encodeURIComponent() for custom values | Prevents accidental nested fragments |
| Form-style query string | URLSearchParams | Handles + and separator rules consistently |
For example, a full URL can contain a query string, and a query string can contain another URL:
const target = 'https://example.com/report?range=7d&format=json#summary'
const share = new URL('https://app.example.com/share')
share.searchParams.set('next', target)
console.log(share.toString())
In this case you do not need to manually call encodeURIComponent() because URLSearchParams encodes the next value for you. If you build the string by concatenation, you must encode the nested URL yourself.
Backend decoding rules
The frontend and backend must agree on which layer is encoded. A common bug is decoding the whole URL before parsing the query string:
// Risky: decoding the whole URL can turn encoded separators back into real separators.
const decoded = decodeURIComponent('/search?q=a%3Db%26c%3Dd')
After decoding, a%3Db%26c%3Dd becomes a=b&c=d, and a later parser may treat &c=d as a separate parameter.
The safer order is:
- Parse the URL into components.
- Read a single query parameter value.
- Decode that one value once.
- If the decoded value contains JSON, Base64, or another URL, process that inner format separately.
This matters for OAuth redirects, password reset links, invite links, search filters, analytics campaign URLs, and any API endpoint that accepts a callback URL.
Double encoding
Double encoding happens when you encode an already encoded value.
hello world
hello%20world
hello%2520world
%25 is the encoded form of %. If you see %2520, a value that already contained %20 was encoded again.
Before decoding or encoding, ask which layer you are working on:
- raw user input
- query parameter value
- full URL
- JSON payload containing a URL
- redirect URL inside another URL
Spaces: %20 vs +
In URLs, spaces are commonly encoded as %20. In form-encoded query strings, spaces may appear as +.
Do not manually replace spaces unless you know the target format. URLSearchParams is usually the safest option for query strings generated in JavaScript.
SEO and analytics parameters
URL encoding also affects SEO and analytics. A malformed query string can make two URLs look different when they should be the same, or make campaign parameters disappear before analytics receives them.
Examples to test:
utm_campaign=summer saleshould be encoded consistently.- A landing page URL inside
redirect=should not leak its ownutm_*parameters into the outer URL. - A canonical URL should not contain a double-encoded path such as
%252F. - A search page should avoid creating endless crawlable combinations of broken query parameters.
If you are debugging traffic attribution, compare the raw URL, the parsed query parameters, and the decoded value. Do not decode the entire URL repeatedly.
Debugging checklist
When a URL parameter breaks:
- Identify whether you are encoding a full URL or one component.
- Use
encodeURIComponent()for query values. - Use
URLSearchParamswhen building query strings. - Check for double encoding such as
%252For%2520. - Decode one layer at a time.
- Be careful with nested redirect URLs.
- Test values containing
&,=,?,#, spaces, Chinese/Japanese text, and emoji.
Related tools
Use the URL Encoder/Decoder to inspect a single value and the URL Parameter Parser to see how a full query string is split. If a parameter contains JSON, validate it with the JSON Formatter after decoding the URL layer.