decodeURIComponent()

The inverse of encodeURIComponent. Its one hard edge is that malformed input throws rather than degrading, which matters because the input usually comes from a URL you did not write.

Global functionES3 (1999)Live demo
Common call
decodeURIComponent(param)
Returns
the decoded string
Replaces
the deprecated unescape()
Watch out
a stray % throws URIError; + is NOT decoded as a space
decodeURIComponent(stringstring — The percent-encoded text. decodeURI leaves reserved sequences such as %26 and %2F encoded; decodeURIComponent decodes everything.type: string · required)
→ string

Demo

Live evaluation
Try:
Inputs
sstringencoded text, e.g. a%20b
Output
[decodeURIComponent('a%20b'), decodeURI('a%20b')]
['a b', 'a b']

The pair is [decodeURIComponent, decodeURI]. The second case shows the asymmetry that mirrors the encode side: %26 becomes & under decodeURIComponent and stays %26 under decodeURI, because decodeURI deliberately refuses to decode anything that would change a URL structure. The fourth case catches people constantly — a + stays a plus sign, because the plus-means-space convention belongs to form encoding, not to URLs. The last case throws: a lone % is malformed, and there is no lenient mode.

Parameters

NameTypeRequiredDescription
stringstringyesThe percent-encoded text. decodeURI leaves reserved sequences such as %26 and %2F encoded; decodeURIComponent decodes everything.

Return value

string — The decoded string. Throws URIError for a malformed sequence — a % not followed by two hex digits, or bytes that are not valid UTF-8.

Common patterns

Read a query parameter
URLSearchParams decodes for you, including +.
const q = new URL(href).searchParams.get('q');
Decode defensively
Malformed input from a URL is common.
let v;
try { v = decodeURIComponent(raw); } catch { v = raw; }
Handle form encoding
Replace + before decoding, if you must do it by hand.
decodeURIComponent(raw.replace(/\+/g, ' '));

Examples

1. A space
decodeURIComponent('a%20b')
Returns
'a b'
2. Reserved decoded
decodeURIComponent('%26')
Returns
'&'
3. decodeURI keeps it
decodeURI('%26')
Returns
'%26'
4. UTF-8
decodeURIComponent('%E4%B8%AD')
Returns
'中'
5. + is left alone
decodeURIComponent('a+b')
Returns
'a+b'
6. A stray % throws
decodeURIComponent('%')
Returns
URIError: URI malformed

Pitfalls

1. Malformed input throws URIError
A lone % — from truncated input, a hand-edited URL, or a value that was never encoded — crashes rather than passing through. Since the input is almost always from outside your control, decoding without a try/catch is a reliable way to break a page on a bad link.
Crashes
decodeURIComponent('100%')
URIError: URI malformed
Guard it
try { decodeURIComponent(raw); } catch { /* use raw */ }
survives
2. It does not turn + into a space
Query strings produced by HTML forms encode spaces as +, and this function leaves them as plus signs. Search terms then arrive with visible pluses. URLSearchParams applies the form rules and is the right tool for reading query strings.
Plus survives
decodeURIComponent('hello+world')
'hello+world'
URLSearchParams
new URLSearchParams('q=hello+world').get('q')
'hello world'
3. Double-decoding is a security bug
Decoding twice turns %252F into a literal slash, which can defeat a path check that ran between the two decodes. Decode exactly once, at the boundary, and never decode a value you have already decoded.
Escapes the check
decodeURIComponent(decodeURIComponent('%252F'))
'/'
Decode once
decodeURIComponent('%252F')
'%2F'
4. decodeURI cannot undo encodeURIComponent
They are not a pair. decodeURI refuses to decode reserved sequences, so a value escaped with encodeURIComponent comes back still containing %26 and %2F. Match each function with its own inverse.
Half decoded
decodeURI(encodeURIComponent('a&b'))
'a%26b'
Matching pair
decodeURIComponent(encodeURIComponent('a&b'))
'a&b'

When to use

Use it
  • Decoding one value you escaped with encodeURIComponent
  • Reading a percent-encoded path segment
  • Tidying a display string that arrived encoded
Reach for something else
  • Reading query strings → URLSearchParams, which handles +
  • The input may be malformed → wrap it in try/catch
  • A whole URL → decodeURI, or leave it encoded
  • unescape() → deprecated and wrong for UTF-8

Notes

Complexity
O(n) in the length of the string
Return
A new string; throws rather than returning a partial result
CPython impl
V8: Builtins-global-decodeuricomponent
Memory
Allocates the result string
Thread-safe
Single-threaded

FAQ

Because % begins an escape sequence, so a % not followed by two hex digits is malformed input and the specification requires a URIError. There is no lenient mode — wrap it, or validate first.

const safe = s => { try { return decodeURIComponent(s); } catch { return s; } };

History

ES1
unescape shipped alongside escape, using Latin-1.
ES3
decodeURI and decodeURIComponent added with UTF-8 support and strict validation.