How to URL encode and decode text, and when to use encodeURI vs encodeURIComponent
Paste text, pick an encoder, copy the result. Choosing between the two encoders is the part that trips people up, so most of this page is about that.
A URL has a grammar. Slashes separate path segments, a question mark starts the query, an ampersand separates parameters, and a hash starts the fragment. When your data contains one of those characters, the parser on the other end has no way to know you meant it literally. Percent-encoding is how you say so: replace the character with % followed by the hex value of each of its UTF-8 bytes.
How it works
- Pick a mode.
encodeURIComponentfor a single value you are about to drop into a query string or path segment.encodeURIfor a whole address you want to stay clickable. - Type in the left pane to encode. Paste percent-encoded text in the right pane to decode. Both panes are live, and either one updates the other.
- Copy the side you need, or hit Swap to move the encoded output back into the input for a second pass.
The difference between the two encoders
Both functions leave the unreserved set alone: letters, digits, and - _ . ~. They differ on the reserved delimiters, the characters that carry structural meaning in a URL.
encodeURI preserves : / ? # [ ] @ ! $ & ' ( ) * + , ; = because a complete URL needs them to still be a URL. Feed it https://example.com/a b?q=1 and you get https://example.com/a%20b?q=1, which still resolves.
encodeURIComponent escapes those same delimiters. That is the whole point: your value is not supposed to be structure. Encoding the search term rock & roll with it gives rock%20%26%20roll, and dropping that into ?q= works. Encoding it with encodeURI gives rock%20&%20roll, and now the server sees a parameter named roll that you never sent.
The short version: one URL, one call to encodeURI. One value inside a URL, one call to encodeURIComponent. Never call either one twice on the same string, or %20 turns into %2520 and you get double-encoding bugs that only show up in production logs.
Why decoding throws
decodeURIComponent('%E0%A4%A') raises URIError: URI malformed. Nothing exotic is going on. %E0 announces a three-byte UTF-8 sequence, the second byte arrives, and the third is cut off at %A. There is no character to produce, so the function refuses rather than guessing.
The same error fires when a percent sign is followed by anything other than two hex digits, which is what happens when a raw % from user input reaches a decoder untouched. Discount codes are a good source of these: SAVE50% pasted into a query string decodes to an exception. The tool above catches the throw and tells you the encoding is broken instead of blanking the output.
Non-ASCII is bytes, not characters
Percent-encoding operates on bytes, and the byte encoding for URLs is UTF-8. So é becomes %C3%A9, two bytes, and a rocket emoji becomes %F0%9F%9A%80, four bytes. This is why an encoded string of Japanese or Arabic text looks so much longer than the original. It also explains a common mixed-encoding failure: text that was percent-encoded from Latin-1 gives %E9 for é, and a UTF-8 decoder cannot read that at all.
One character will not encode: an unpaired surrogate, the orphaned half of an emoji that survived a bad string slice. JavaScript has no UTF-8 bytes to emit for it, so encodeURIComponent throws too. That case gets its own message in the tool.
Spaces, plus signs, and forms
Both encoders turn a space into %20. The plus sign means space only in application/x-www-form-urlencoded data, which is what browsers submit for a GET form and what most query-string parsers expect. In a path segment, + is just a plus. If you build query strings with URLSearchParams, note that it uses the form encoding and will write + where encodeURIComponent writes %20. A form-encoding parser reads both correctly, so pick one and stay consistent.