Skip to main content

URL Encoder and Decoder

Percent-encode and decode URLs with an explicit choice between encodeURI and encodeURIComponent, plus form-encoding mode where "+" means space.

Your data never leaves the browserUpdated: July 2026

Encodes a single query value or path segment. Escapes delimiters such as & = ? too.

Key Takeaways

  • encodeURIComponent encodes a single piece (a query value, one path segment); encodeURI preserves delimiters for a complete URL. Swapping them breaks links silently.
  • In form encoding (application/x-www-form-urlencoded) "+" means a space, but decodeURIComponent does not know that.
  • A malformed percent-escape such as a lone "%" makes decodeURIComponent throw a URIError, which will blank the page if left uncaught.
  • Percent-encoding works on UTF-8 bytes, not characters: "ş" is one character but encodes to two bytes, %C5%9F.

What actually separates encodeURI from encodeURIComponent

JavaScript ships two encoding functions and the difference between them is not a matter of taste. encodeURIComponent escapes every delimiter that could appear inside a URL part: ampersand, equals, question mark, slash. encodeURI assumes you are encoding a whole address and leaves those delimiters alone, because escaping them would destroy the URL structure. A tool that offers only one of them is wrong in half of all use cases, which is why the mode here is an explicit choice.

javascript
const value = 'a&b=c'; encodeURIComponent(value); // 'a%26b%3Dc'  -> correct for a query VALUEencodeURI(value);          // 'a&b=c'      -> & and = preserved // The practical consequence:'/search?q=' + value;                     // '/search?q=a&b=c'// The server reads q=a plus a separate parameter b=c. '/search?q=' + encodeURIComponent(value); // '/search?q=a%26b%3Dc'// The server reads q with the value 'a&b=c'. This is the correct one.
InputencodeURIComponentencodeURIWhich one to use
a&b=ca%26b%3Dca&b=cComponent, if it is a query value
/path/sub%2Fpath%2Fsub/path/subFull URL, if it is an address
https://a.com/x yhttps%3A%2F%2Fa.com%2Fx%20yhttps://a.com/x%20yFull URL for a complete address
payment#1payment%231payment#1Component, if it is a fragment value
a+ba%2Bba+bComponent when the plus is a real plus
The rule is short: encoding one piece means component mode, encoding an entire address means full URL mode.

Form encoding and the plus-sign trap

HTML forms and a great many query strings use the application/x-www-form-urlencoded format. It is a close relative of percent-encoding with one critical difference: a space is written as + rather than %20. As a result the plus character now carries two possible meanings. If you need to transmit a literal plus, it has to be encoded as %2B, otherwise the receiving side reads a space.

javascript
// Encoding: in form mode a space becomes +, a real plus becomes %2BencodeURIComponent('one two+three').replace(/%20/g, '+');// 'one+two%2Bthree' // Decoding: the + to space conversion must happen BEFORE decodeURIComponent,// or a literal %2B turns into + first and is then wrongly read as a space.const input = 'one+two%2Bthree'; decodeURIComponent(input);                    // 'one+two+three'  <-- wrongdecodeURIComponent(input.replace(/\+/g, ' ')); // 'one two+three'  <-- right // Phone numbers are the classic casualty of this bug:decodeURIComponent('%2B905551234567'); // '+905551234567'new URLSearchParams('tel=+905551234567').get('tel'); // ' 905551234567' <-- plus lost

Warning

If a query parameter carries an international phone number, base64 output, or a signature, encode the plus as %2B without exception. This bug usually stays invisible in local testing, because local-format numbers contain no plus at all.

Non-ASCII text is encoded as UTF-8 bytes

Percent-encoding escapes bytes, not characters. Every non-ASCII character is first converted to UTF-8 bytes, and each byte is then written as its own %XX sequence. Turkish letters occupy two bytes in UTF-8, so a single letter expands into two percent-escapes. That makes an encoded URL harder to read by eye, but the behaviour is entirely predictable.

javascript
encodeURIComponent('ş'); // '%C5%9F'   (two bytes)encodeURIComponent('ı'); // '%C4%B1'encodeURIComponent('İ'); // '%C4%B0'encodeURIComponent('ğ'); // '%C4%9F'encodeURIComponent('a'); // 'a'         (ASCII is left alone) encodeURIComponent('çiçek'); // '%C3%A7i%C3%A7ek' // An emoji takes four bytes:encodeURIComponent('🚀'); // '%F0%9F%9A%80'

The hostname is a separate story. In an address such as https://çiçek.com the domain is not percent-encoded at all: it is carried as punycode, and the browser rewrites it to https://xn--iek-3lac.com. Percent-encoding applies only to the path, query and fragment. If you work with internationalised domain names, normalise both representations before comparing them server-side.

Errors raised while decoding

decodeURIComponent does not shrug off invalid input, it throws a URIError. A stray percent sign, a truncated byte sequence, or an invalid UTF-8 combination all trigger it. In a React application an uncaught error of this kind reaches the error boundary and clears the screen. This tool always runs decoding inside a try/catch and reports the failure as an ordinary validation result.

javascript
decodeURIComponent('%');        // URIError: URI malformeddecodeURIComponent('%E0%A4%A'); // URIError: third byte is missingdecodeURIComponent('%ZZ');      // URIError: not valid hex // The safe form:function safeDecode(text) {  try {    return { ok: true, value: decodeURIComponent(text) };  } catch {    return { ok: false, error: 'Malformed percent-escape' };  }} // Encoding has exactly one failure mode: an unpaired surrogate.encodeURIComponent('\uD800'); // URIError: no UTF-8 representation

Frequently Asked Questions

Is the text I paste sent to a server?
No. Encoding and decoding both run in JavaScript inside your browser. No network request is made, and once the page has loaded you can disconnect entirely and the tool keeps working. That matters when you are inspecting URL fragments that contain signatures or session tokens.
When should I pick full URL mode?
Choose full URL mode when you hold a complete address including its scheme and only want genuinely invalid characters such as spaces cleaned up. If you are encoding the value of a single query parameter or one path segment, use component mode, otherwise an ampersand inside your value behaves like a new parameter.
What happens if I encode a string twice?
The percent sign itself gets encoded, so a sequence like %20 becomes %2520. Double encoding is common where proxies or redirect layers are chained together. The tell-tale sign is output containing escapes that start with %25: if you see those, the input was already encoded and needs one round of decoding.
Is URLSearchParams a better choice?
For building a whole query string, yes, because it escapes values for you and follows form-encoding rules. Be aware that it writes spaces as plus signs and reads incoming plus signs back as spaces. When you are encoding a single value by hand, encodeURIComponent is more predictable.
Why are some characters never encoded?
RFC 3986 defines letters, digits and the hyphen, underscore, period and tilde as unreserved characters. They need no encoding, and the escaped and raw forms mean the same thing. encodeURIComponent additionally leaves exclamation marks, asterisks, single quotes and parentheses untouched.