Base64 encode/decode · Guide
Base64 Encode Browser: atob, btoa and the Unicode Problem
Browsers have shipped `btoa` and `atob` since the 1990s. They still work, they are still the fastest path — and they still break on any character outside Latin-1, which catches nearly everyone once.
The built-ins
btoa(s) encodes a binary string to Base64; atob(s) decodes back. The names come from "ASCII to binary" and the reverse, which is confusing in the opposite direction to what you would guess.
They operate on strings where each character represents one byte, values 0-255. That model is fine for binary data expressed that way and wrong for ordinary Unicode text.
The Unicode failure
btoa("café") throws InvalidCharacterError, because é is above U+00FF. Convert to UTF-8 bytes first and the problem disappears:
const toBase64 = (str) =>
btoa(String.fromCharCode(...new TextEncoder().encode(str)));
const fromBase64 = (b64) =>
new TextDecoder().decode(
Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))
);
toBase64('café'); // 'Y2Fmw6k='
fromBase64('Y2Fmw6k='); // 'café'atob also throws on malformed input — bad padding, or - and _ from a base64url string. Wrap it in try/catch whenever the input comes from a user or an API.
Files and large data
For files, FileReader.readAsDataURL gives you a data URI with the Base64 already in it, handled natively and without stack limits:
const reader = new FileReader();
reader.onload = () => {
const b64 = reader.result.split(',')[1]; // strip the data: prefix
};
reader.readAsDataURL(file);Avoid String.fromCharCode(...bigArray) for large buffers — the spread exceeds the maximum argument count and throws. Chunk it, or use the FileReader route.
Frequently asked questions
Why does btoa throw InvalidCharacterError?
The string contains characters above U+00FF. Convert to UTF-8 bytes with TextEncoder first.
Are atob and btoa deprecated?
No. They are still standard and widely used; they simply predate Unicode-aware APIs.
How do I decode a JWT part in the browser?
Translate base64url to standard first: replace "-" with "+", "_" with "/", then pad to a multiple of 4.
Ready to try it?
Open the free browser-based Base64 encode/decode and apply what you just read — no sign-up, runs locally.
Open the Base64 encode/decode tool