decodeURI()
Decodes a complete URL's percent-escapes while leaving reserved delimiters encoded.
decodeURI(encodedURI)- Added in JavaScript ECMAScript 3 (1999)
Parameters
| Parameter | Type | Description |
|---|---|---|
$encodedURI | string | A complete, encoded Uniform Resource Identifier. Other values are converted to strings. |
Return value
stringA new string with percent-escapes decoded as UTF-8, except those representing the reserved characters ; / ? : @ & = + $ , #.
Errors
Throws URIError if a % is not followed by two hexadecimal digits, or if the escape sequence does not encode a valid UTF-8 character.
decodeURI() undoes encodeURI() and nothing more. It deliberately refuses to decode escapes for the reserved characters ; / ? : @ & = + $ , #, so %2F stays %2F and %3F stays %3F. That preserves the URL's structure: a slash that was escaped because it was data does not suddenly become a path separator.
What it is good for
Turning an encoded URL back into something readable — a link label, a breadcrumb, a log line, a diff. It is a display transformation, not a parsing step.
What it is not good for
Reading a single query value. Those need decodeURIComponent(), or better, URLSearchParams. If you decode a parameter with decodeURI() you will get back a value that still contains %26 and %3D.
It throws
A malformed escape raises a URIError: a bare %, a non-hex pair, or a truncated multi-byte sequence. URLs from untrusted sources routinely contain these, so wrap the call and fall back to showing the raw string.
Note also that a decoded URL is not necessarily a valid one — decoding is not re-encoding, and a display string should not be turned back into a request without running it through encodeURI() again.
Example
When to use it
- Render a human-readable version of an encoded URL in a link or a log.
- Compare two URLs after normalising their escaping.
- Show a filename from an encoded path without breaking the path structure.
Frequently asked questions
What is the difference between decodeURI and decodeURIComponent?
decodeURI leaves escapes for the reserved characters ; / ? : @ & = + $ , # encoded, while decodeURIComponent decodes everything. Use decodeURI on a complete URL and decodeURIComponent on an individual value.
Why does decodeURI not decode %2F?
Because %2F represents a slash, which is a URI delimiter — decoding it would change the URL's path structure. That preservation is the entire reason the two decode functions are separate.
How do I stop decodeURI from throwing on user input?
Wrap the call in try/catch and return the original string on failure, since a bare % or a truncated UTF-8 escape raises URIError. Untrusted or hand-edited URLs contain these often enough that an unguarded call is a reliable crash.
More examples
A clean round trip
encodeURI escapes the spaces and Cyrillic characters; decodeURI restores them exactly. The pair is symmetric as long as the original contained no literal percent signs.
const original = "https://example.com/отчёт 2024/final draft.pdf";
const encoded = encodeURI(original);
console.log(encoded);
console.log(decodeURI(encoded));
console.log(decodeURI(encoded) === original); // trueWhy the reserved set stays encoded
The encoded slash keeps "reports/2024" as a single path segment. decodeURI respects that; decodeURIComponent destroys it by turning the escape into a real separator.
// A path segment whose data contains a slash
const seg = encodeURIComponent("reports/2024");
const url = `https://example.com/files/${seg}`;
console.log(url);
console.log(decodeURI(url)); // %2F preserved — still one segment
console.log(decodeURIComponent(url)); // %2F decoded — now two segments
console.log(new URL(url).pathname.split("/").length);Displaying untrusted URLs safely
The second and third inputs both throw URIError — one has a bare percent sign, the other an overlong UTF-8 encoding — so the helper falls back to the raw string instead of failing the render.
function readable(url) {
try {
return decodeURI(url);
} catch {
return url;
}
}
console.log(readable("https://example.com/a%20b"));
console.log(readable("https://example.com/100%"));
console.log(readable("https://example.com/%C0%80"));
Discussion