DEV Community

ze he
ze he

Posted on • Originally published at aiforeverthing.com

URL Encoding Explained: encodeURIComponent vs encodeURI (and When to Use Each)

URL Encoding Explained: encodeURIComponent vs encodeURI

URL encoding is one of those things that's easy to get wrong — and when you do, you get mysterious 400 Bad Request errors or mangled query strings. This guide covers everything you need to know.

Quick tool: URL Encoder/Decoder — encode or decode URLs instantly in your browser.


What Is URL Encoding?

URLs can only contain a limited set of safe ASCII characters. Any other characters — spaces, Chinese characters, special symbols — must be "percent-encoded" using the format %XX where XX is the hex code of the character:

Space    →  %20
&        →  %26
=        →  %3D
#        →  %23
/        →  %2F
Enter fullscreen mode Exit fullscreen mode

So hello world becomes hello%20world in a URL.


The Two JavaScript Functions

encodeURIComponent() — For parameter values

Encodes everything except: A-Z a-z 0-9 - _ . ! ~ * ' ( )

encodeURIComponent('hello world')  // 'hello%20world'
encodeURIComponent('a & b = c')   // 'a%20%26%20b%20%3D%20c'
encodeURIComponent('https://example.com') // 'https%3A%2F%2Fexample.com'
Enter fullscreen mode Exit fullscreen mode

encodeURI() — For complete URLs

Preserves URL structure characters: ; , / ? : @ & = + $ #

encodeURI('https://example.com/search?q=hello world')
// 'https://example.com/search?q=hello%20world'
// Note: only the space was encoded, not ? or =
Enter fullscreen mode Exit fullscreen mode

The Rule of Thumb

Use encodeURIComponent for values, encodeURI for complete URLs.

// WRONG — encodeURI won't encode & and = inside the value
const badUrl = 'https://api.com?' + encodeURI('key=a&b=c');
// 'https://api.com?key=a&b=c'  ← & and = are preserved, breaks param parsing

// RIGHT — encodeURIComponent encodes everything
const goodUrl = `https://api.com?q=${encodeURIComponent('a & b')}`;
// 'https://api.com?q=a%20%26%20b'  ✓
Enter fullscreen mode Exit fullscreen mode

Building Query Strings Correctly

Manual approach

const params = {
  q: 'hello world',
  lang: 'en',
  source: 'https://example.com'
};

const queryString = Object.entries(params)
  .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
  .join('&');

// q=hello%20world&lang=en&source=https%3A%2F%2Fexample.com
Enter fullscreen mode Exit fullscreen mode

Using URLSearchParams (recommended)

const params = new URLSearchParams({
  q: 'hello world',
  lang: 'en',
  source: 'https://example.com'
});

const url = `https://api.example.com/search?${params}`;
// https://api.example.com/search?q=hello+world&lang=en&source=https%3A%2F%2Fexample.com
Enter fullscreen mode Exit fullscreen mode

Note: URLSearchParams uses + for spaces (form-encoding), while encodeURIComponent uses %20. Both are valid but inconsistent — be aware when mixing them.

Decoding query strings

const url = new URL('https://example.com/search?q=hello%20world&lang=en');
const query = url.searchParams.get('q'); // 'hello world' (auto-decoded)

// Or manually:
const encoded = 'hello%20world%20%26%20more';
console.log(decodeURIComponent(encoded)); // 'hello world & more'
Enter fullscreen mode Exit fullscreen mode

Common Mistakes

Double-encoding

const value = 'hello world';

// Encoding once: hello%20world ✓
const once = encodeURIComponent(value);

// Encoding twice: hello%2520world ✗
// %20 itself gets encoded → %2520
const twice = encodeURIComponent(encodeURIComponent(value));
Enter fullscreen mode Exit fullscreen mode

To fix double-encoding, decode first, then encode:

function safeEncode(str) {
  // Decode if already encoded, then encode cleanly
  try {
    return encodeURIComponent(decodeURIComponent(str));
  } catch {
    return encodeURIComponent(str);
  }
}
Enter fullscreen mode Exit fullscreen mode

Using + for spaces vs %20

// application/x-www-form-urlencoded (HTML forms)
'hello world'  'hello+world'

// RFC 3986 percent-encoding (most APIs)
'hello world'  'hello%20world'

// When decoding form data on the server:
decodeURIComponent('hello+world')  // 'hello+world' ← + not decoded!
decodeURIComponent('hello+world'.replace(/\+/g, ' '))  // 'hello world' ✓
Enter fullscreen mode Exit fullscreen mode

Server-Side Encoding (Node.js)

const { URL, URLSearchParams } = require('url');

// Building URLs safely
const apiUrl = new URL('https://api.example.com/search');
apiUrl.searchParams.set('q', 'hello world & more');
apiUrl.searchParams.set('page', '1');
console.log(apiUrl.toString());
// https://api.example.com/search?q=hello+world+%26+more&page=1

// Express.js — query params are auto-decoded
app.get('/search', (req, res) => {
  const query = req.query.q; // Already decoded by Express
  // 'hello world & more'
});
Enter fullscreen mode Exit fullscreen mode

Quick Reference: What Gets Encoded?

Character encodeURI encodeURIComponent
Space %20 %20
/ (preserved) %2F
? (preserved) %3F
& (preserved) %26
= (preserved) %3D
# (preserved) %23
@ (preserved) %40
+ (preserved) %2B

Summary

  • Use encodeURIComponent for values inside URLs (query params, path segments)
  • Use encodeURI for complete URLs when you want to preserve structure
  • Prefer URLSearchParams for building query strings
  • Prefer new URL() for parsing and reading URL components
  • Watch out for + vs %20 when mixing form-encoding and percent-encoding
  • Never double-encode — decode first if you're unsure

For quick encoding/decoding: URL Encoder/Decoder Online

Top comments (0)