JavaScript7 min read

How to Decode a URL in JavaScript (Complete Guide)

JavaScript provides decodeURIComponent() to decode individual URI components and decodeURI() to decode complete URIs. Use try-catch to handle malformed URIs. The URL and URLSearchParams APIs provide safer alternatives for parsing URLs.

decodeURIComponent() — Decode a URI Component

decodeURIComponent() is the primary function for decoding URL-encoded strings in JavaScript. It decodes all percent-encoded sequences, converting them back to their original characters. Use this function when decoding query parameter values, path segments, or any single component of a URI.

// Basic decoding
console.log(decodeURIComponent('hello%20world'));
// "hello world"

console.log(decodeURIComponent('price%3D10%26qty%3D2'));
// "price=10&qty=2"

// Decoding Unicode characters
console.log(decodeURIComponent('caf%C3%A9'));
// "cafe" (with accent)

console.log(decodeURIComponent('%E4%B8%AD%E6%96%87'));
// Chinese characters

// Decoding a query parameter value from a URL
const url = 'https://example.com/search?q=C%2B%2B%20%26%20Java';
const params = url.split('?')[1];
const value = params.split('=')[1];
console.log(decodeURIComponent(value));
// "C++ & Java"

decodeURIComponent() decodes all percent-encoded sequences, including those for reserved characters like %2F (/) and %3F (?). This is correct behavior when working with individual URI components but can cause problems if applied to a full URL.

decodeURI() — Decode a Complete URI

decodeURI() decodes a complete URI while preserving its structure. Unlike decodeURIComponent(), it does not decode sequences that represent reserved URI characters such as %2F (/), %3F (?),%23 (#), and %26 (&).

// decodeURI preserves URI structure
console.log(decodeURI('https://example.com/my%20page?q=hello%20world'));
// "https://example.com/my page?q=hello world"
// Spaces are decoded, but /, ?, and = are preserved

// Compare with decodeURIComponent on a full URL
console.log(decodeURIComponent('https%3A%2F%2Fexample.com%2Fpath'));
// "https://example.com/path" - correctly decodes if the whole URL was encoded

// decodeURI won't decode reserved character sequences
console.log(decodeURI('path%2Fto%2Ffile'));
// "path%2Fto%2Ffile" - %2F is NOT decoded because / is reserved
console.log(decodeURIComponent('path%2Fto%2Ffile'));
// "path/to/file" - %2F IS decoded

Use decodeURI() when you want to make a URL more readable (for display purposes, for example) without altering its structure. For most programmatic use cases, you will want decodeURIComponent() applied to individual components.

Handling Malformed URIs

Both decodeURI() and decodeURIComponent() throw a URIError when they encounter invalid percent-encoded sequences. This happens with lone percent signs, incomplete sequences, or invalid UTF-8 byte sequences. Always wrap decoding operations in a try-catch block when working with user-supplied or external URLs.

// These throw URIError: URI malformed
try {
  decodeURIComponent('%');         // lone percent
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

try {
  decodeURIComponent('%2');        // incomplete sequence
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

// Safe decoding function
function safeDecode(str) {
  try {
    return decodeURIComponent(str);
  } catch (e) {
    console.warn('Failed to decode:', str);
    return str; // return original string on failure
  }
}

// Fix malformed percent sequences before decoding
function fixAndDecode(str) {
  // Replace lone % with %25 (encoded percent sign)
  const fixed = str.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
  return decodeURIComponent(fixed);
}

console.log(fixAndDecode('100% complete'));
// "100% complete"

Using the URL API (Recommended)

The modern URL and URLSearchParams APIs provide a safer and more structured way to parse and decode URLs. They handle encoding and decoding automatically, reducing the risk of errors.

// Parse a URL and access its components (automatically decoded)
const url = new URL('https://example.com/path%20here?q=hello%20world&lang=en');

console.log(url.pathname);  // "/path here" (decoded)
console.log(url.search);    // "?q=hello%20world&lang=en" (raw)

// URLSearchParams automatically decodes parameter values
console.log(url.searchParams.get('q'));     // "hello world"
console.log(url.searchParams.get('lang')); // "en"

// Iterate over all parameters
for (const [key, value] of url.searchParams) {
  console.log(key, '=', value);
}
// q = hello world
// lang = en

// URLSearchParams handles + as space (form encoding)
const formParams = new URLSearchParams('q=hello+world&lang=en');
console.log(formParams.get('q'));  // "hello world"

// Building URLs with automatic encoding
const newUrl = new URL('https://example.com/search');
newUrl.searchParams.set('q', 'C++ & Java');
newUrl.searchParams.set('page', '1');
console.log(newUrl.toString());
// "https://example.com/search?q=C%2B%2B+%26+Java&page=1"

Common Decoding Mistakes

Mistake 1: Decoding a full URL with decodeURIComponent(). This can break the URL structure if it contains encoded reserved characters. A %2F in a query value would become a /, potentially changing the URL's meaning.

Mistake 2: Double decoding. If a string has been decoded once, decoding it again may produce unexpected results or errors. For example, the string %2520 decodes first to %20, then to a space. If you only expect one level of encoding, double decoding corrupts the data.

// Double decoding problem
const encoded = '%2520'; // This is an encoded %20
console.log(decodeURIComponent(encoded));  // "%20" (correct - one level)
console.log(decodeURIComponent(decodeURIComponent(encoded)));  // " " (double decoded!)

// Check if a string needs decoding before decoding it
function needsDecoding(str) {
  return str !== decodeURIComponent(str);
}

Mistake 3: Not handling the + sign. decodeURIComponent()does not convert + to spaces. If you are decoding form-urlencoded data, you need to replace + with spaces first, or use URLSearchParams which handles this automatically.

// decodeURIComponent does NOT decode + as space
console.log(decodeURIComponent('hello+world'));
// "hello+world" (not "hello world"!)

// Fix: replace + before decoding
function decodeFormValue(str) {
  return decodeURIComponent(str.replace(/\+/g, ' '));
}
console.log(decodeFormValue('hello+world'));
// "hello world"

// Or use URLSearchParams (handles + automatically)
const params = new URLSearchParams('q=hello+world');
console.log(params.get('q'));
// "hello world"

Related Articles

Try Our Free Tools