Base64-encoding JSON comes up more often than you'd expect — but it has a sharp edge that catches almost everyone the first time: plain btoa() breaks the moment your data has an emoji in it.
1. Common Reasons to Base64-Encode JSON
Three cases come up constantly: passing structured data through a URL query parameter (raw JSON's braces and quotes aren't URL-safe), the payload section of a JWT, and squeezing structured data into a field or system that only accepts plain text.
2. Why Plain btoa() Breaks on Unicode
JavaScript's btoa() only handles single-byte (Latin1) characters. The moment your JSON contains an emoji, accented letters, or CJK text, it throws InvalidCharacterError. The fix is to UTF-8 encode the string first, then Base64-encode the resulting bytes — not the raw string.
3. The UTF-8-Safe Encode/Decode Pattern
The reliable version is: btoa(unescape(encodeURIComponent(str))) to encode, and decodeURIComponent(escape(atob(str))) to decode. It's an odd-looking pair of function calls, but it correctly round-trips any Unicode content, not just ASCII.
4. Base64 Is Encoding, Not Encryption
Anyone can decode a Base64 string in one line — it provides zero confidentiality. If the JSON contains anything sensitive, encrypt it before encoding; don't rely on Base64 to hide the contents.
5. Handle Decode Failures Gracefully
Input from users or URLs can be malformed — not valid Base64, or valid Base64 that doesn't decode to valid JSON. Wrap the decode-then-parse step in a try/catch and fail with a clear error message rather than letting an uncaught exception take down the page.