UTF-8 Encoder Decoder: Practical Tips and Common Mistakes
Reviewed by the OnlineFree.app team · Updated
Key points
- UTF-8 uses 1 to 4 bytes per code point, so size fields in bytes, not characters.
- An emoji is one code point but two UTF-16 code units, which breaks naive length checks.
- The validator reports the offset and reason for every illegal byte instead of silently inserting U+FFFD.
- Valid UTF-8 can still display as mojibake when it was decoded with the wrong charset.
- Percent-encoding applies to UTF-8 bytes; encode once, or %20 becomes %2520.
What a UTF-8 encoder/decoder actually does
A UTF-8 encoder converts text into its byte representation; a UTF-8 decoder converts bytes back into text. UTF-8 Encoder Decoder does both, plus a strict byte-by-byte validity check, in a single browser tab with no login and no upload step.
The tool has three modes: text to UTF-8 hexadecimal bytes, hexadecimal bytes back to text, and text to percent-encoding. Paste `你好 🌍` in the first mode and you get `E4 BD A0 E5 A5 BD 20 F0 9F 8C 8D` — six bytes for the two CJK characters, one for the space, four for the globe. UTF-8 is variable width: 1 byte for ASCII, 2 for most Latin and Cyrillic letters, 3 for CJK and most symbols, 4 for emoji and rarer scripts.
Each leading byte declares its own length — 110xxxxx means one continuation byte follows, 1110xxxx means two, 11110xxx means three, and every continuation byte must match 10xxxxxx. That self-describing structure is why a decoder can validate while it reads, with no code page table and no out-of-band metadata.
Because the work happens with the browser's native TextEncoder and TextDecoder APIs on your own machine, the input never leaves the page. Like the other tools on OnlineFree.app, it keeps working after the first load, which matters when the machine you are debugging sits behind a restrictive proxy.
How many bytes does your text take?
Count bytes, not characters, before you size a database column, a protocol field, or a fixed-width record. The summary line above the results shows three numbers for the same input: UTF-8 byte length, Unicode code point count, and UTF-16 code unit count.
`你好 🌍` is 10 bytes, 4 code points, and 5 UTF-16 code units. The gap comes from the emoji: U+1F30D is one code point but needs a surrogate pair in UTF-16, so anything counting UTF-16 code units sees two. `café` written with a precomposed é is 4 code points and 5 bytes, because é (U+00E9) costs `C3 A9`.
This is the source of most “my string is too long” bugs. Java and JavaScript report length in UTF-16 code units, Python 3 and Rust report code points, and many databases let you choose between a character limit and a byte limit on the same column. Encoding your sample to hex first tells you exactly which budget you are spending.
Hex output also grows fast: each byte costs two characters plus a separator, so a long input — the tool's soft ceiling is 200,000 characters — can produce millions of characters of hex. Past that limit the tool warns you and suggests narrowing the input instead of freezing the tab.
Why does text turn into mojibake?
Classic mojibake happens when UTF-8 bytes are decoded with a single-byte charset such as Windows-1252 or ISO-8859-1. The bytes are fine; the reader is misconfigured. `café` encoded as UTF-8 is `63 61 66 C3 A9`, and reading `C3 A9` as Windows-1252 produces the familiar `café`.
Paste `café` into text-to-hex mode and you get `63 61 66 C3 83 C2 A9`: the mojibake characters were themselves encoded, which is the signature of double encoding. Compare that with the original `63 61 66 C3 A9` and you can see where the extra layer came from — usually a decode step applying Latin-1 and an encode step applying UTF-8, or a library that assumes UTF-8 on output but not on input. As of 2026, most APIs default to UTF-8 but still honour a charset parameter in the Content-Type header, so the mismatch is usually a configuration detail rather than a broken library.
To repair double-encoded text you normally decode the mojibake as Windows-1252 to recover the original bytes, then decode those bytes as UTF-8. The tool cannot do that first step for you, because the mojibake text is itself perfectly valid UTF-8 — but showing you `C3 83 C2 A9` instead of `C3 A9` is the confirmation you need before fixing the code.
One more habitual check: an invisible U+FEFF at the start of a file is the UTF-8 BOM, three bytes `EF BB BF`. Some parsers strip it, some treat it as content and break the first field. If only the first record misbehaves, look at offset 0.
Illegal UTF-8 byte patterns the report flags
The validation panel answers a different question from “is this text readable?” It asks whether a byte string is legal UTF-8 at all, and it names the offset and the reason for each violation, following the encoding rules in RFC 3629.
Five patterns cover almost everything we see. An orphan continuation byte is a lone 0x80–0xBF with no leading byte before it — typical of a stream cut mid-character by a fixed-length buffer or a partial socket read. A truncated sequence is the same accident at the end of the input, such as `E4 BD` where three bytes were expected. Invalid start bytes are 0xC0, 0xC1 and 0xF5–0xFF, which can never begin a UTF-8 sequence. Overlong encodings use more bytes than necessary, like `C0 80` for U+0000. Surrogate halves in the range `ED A0 80` to `ED BF BF` and anything from `F4 90 80 80` upward (beyond U+10FFFF, the highest Unicode code point) are rejected too.
We wrote the validator as a hand-rolled state machine rather than relying on TextDecoder alone. With fatal mode off, the browser silently replaces every bad byte with U+FFFD, and you end up with a “decoded” string plus no idea which byte broke. Strict validation keeps the offset, so you can jump straight to byte 47 of a 200-byte packet.
A passing badge means the bytes are structurally valid UTF-8 — nothing more. Text decoded with the wrong charset produces valid bytes for the wrong characters, and the validator will accept it. Validity is a necessary check, not proof that your pipeline is correct.
Percent-encoding: encode once, correctly
Percent-encoding turns each UTF-8 byte into `%XX`, which is how non-ASCII text travels inside URLs. `é` becomes `%C3%A9` and `你` becomes `%E4%BD%A0`, because the bytes are encoded, not the characters.
The trap is that two conventions share the same syntax. RFC 3986 percent-encoding represents a space as `%20`, while HTML form submission (`application/x-www-form-urlencoded`) uses `+`. A value encoded with one rule and parsed with the other loses its spaces or gains literal plus signs. When a parameter looks right on screen but fails on the server, check which convention each side uses.
Double encoding is the second common mistake. Encoding an already-encoded string escapes the percent sign itself: `%20` becomes `%2520`, because `%` is encoded as `%25`. If you decode a parameter and find a literal `%20` inside it, something in the chain encoded twice. Note also that JavaScript's encodeURIComponent leaves letters, digits and `- _ . ! ~ * ' ( )` untouched — it solves URL syntax, not HTML or SQL escaping.
To check any stage, paste the value into the browser-only UTF-8 workbench and switch to percent-encoding mode. As a final safety net, decoding a truncated or illegal sequence with decodeURIComponent throws a URIError, which is the loud failure you want rather than silent replacement.
Frequently asked questions
Is the UTF-8 Encoder Decoder free to use?
Yes. It is free, requires no account, and the conversion plus validation run entirely inside your browser tab. Your text or hex bytes are never uploaded to a server, so nothing is stored or logged. As with any online tool, avoid pasting production secrets or personal data unless you are comfortable with the browser and device you are using.
Why is one emoji four bytes but two characters in Java?
Because UTF-8 and UTF-16 count different units. An emoji such as 🌍 (U+1F30D) is a single Unicode code point: UTF-8 needs 4 bytes for it and UTF-16 needs 2 code units, a surrogate pair. Java's String.length() and JavaScript's string.length return 2, while Python 3's len() returns 1. The tool shows all three counts at once.
What causes an orphan continuation byte error?
An orphan continuation byte is a byte in the range 0x80–0xBF that appears where a leading byte should be. It usually means data was split mid-character by a fixed-length buffer, a partial network read, or a substring operation on a byte array. The validator reports the exact offset so you can trace where the split happened.
Can the UTF-8 Encoder Decoder repair mojibake?
Not directly, and that is by design. Mojibake such as café is valid UTF-8 for the wrong characters, so decoding it returns those wrong characters. The tool shows you the bytes, which usually reveals double encoding (C3 83 C2 A9 rather than C3 A9). The real fix happens in your code: decode as Windows-1252, then decode the result as UTF-8.
Does a valid result mean my encoding is correct?
No. A passing badge means the bytes form legal UTF-8: no orphan continuation bytes, truncation, overlong forms, surrogate halves, or code points above U+10FFFF. It says nothing about whether those bytes represent the text you expected. Always compare a known sample, such as a CJK character or an emoji, before shipping a fix.