The Difference Between UTF-8, UTF-16, and UTF-32
UTF-8, UTF-16, and UTF-32 are all encodings of the same Unicode standard—they represent the same set of characters but store them in different ways. Choosing the wrong one for your use case leads to wasted space, broken interoperability, and subtle bugs. Here's a clear breakdown of when to use each.
UTF-8
UTF-8 is variable-width: it uses 1–4 bytes per character. It's the right choice for almost everything: web content, APIs, databases, files on disk, and most text in source code. Its ASCII compatibility and space efficiency for Latin-script text make it the global default.
The main downside is that random access to the nth character requires scanning from the start of the string (you can't just multiply by a fixed byte size), but in practice this is rarely a performance bottleneck.
UTF-16
UTF-16 uses 2 bytes for characters in the Basic Multilingual Plane (U+0000–U+FFFF) and 4 bytes (a surrogate pair) for supplementary characters like emoji. It's the internal string representation in JavaScript, Java, C#, and Windows APIs.
When you call "🔥".length in JavaScript and get 2 instead of 1, you're seeing surrogate pairs in action. The fire emoji U+1F525 requires two UTF-16 code units. This trips up many developers. Use Array.from("🔥").length or the spread operator to get the correct character count.
UTF-32
UTF-32 uses exactly 4 bytes per code point, making random access trivial—the nth character is always at byte offset n × 4. The obvious cost is space: even a plain ASCII string is four times as large as its UTF-8 equivalent. UTF-32 is used internally by some text processing libraries and by Python 3 on some platforms, but you'll rarely encounter it in network protocols or file formats.
Which Should You Use?
- Web, APIs, files, databases: UTF-8, always.
- Windows native APIs, .NET, Java, JavaScript internals: UTF-16 is the platform default; understand surrogates.
- Fixed-width string processing in memory: UTF-32 if you need O(1) character access and memory isn't a constraint.
You can look up the UTF-8, UTF-16, and UTF-32 byte representations of any character using CharLookup's search. For example, try looking up U+20AC € to see all three encodings side by side.