Base64 encode/decode · Guide

Base64 to Text JS: Decoding in JavaScript Correctly

Decoding Base64 in JavaScript is one function call — and that function predates Unicode support in the language, which is why so much JavaScript Base64 code is subtly broken.

atob and its blind spot

atob(b64) returns a binary string where each character is one byte. For pure ASCII content that is already the text you want. For anything else it is not:

JavaScript
atob('aGVsbG8=');    // 'hello'      — fine
atob('Y2Fmw6k=');    // 'café'      — wrong, bytes shown as Latin-1

The mangled output is the signature of this bug: correct bytes, wrong interpretation.

The correct decode

Convert the binary string to a byte array, then decode those bytes as UTF-8:

JavaScript
const fromBase64 = (b64) =>
  new TextDecoder().decode(
    Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))
  );

fromBase64('Y2Fmw6k=');   // 'café'

atob throws InvalidCharacterError on malformed input, so wrap it in try/catch whenever the string comes from a user, a URL or an API.

base64url

JWT segments and URL-safe payloads use a different alphabet and usually no padding. Translate before decoding:

JavaScript
const fromBase64Url = (s) => {
  const b64 = s.replace(/-/g, '+').replace(/_/g, '/');
  return fromBase64(b64.padEnd(b64.length + ((4 - (b64.length % 4)) % 4), '='));
};

Node.js

Node has no atob need — Buffer handles both directions and both alphabets, with the encoding stated explicitly:

JavaScript
Buffer.from('Y2Fmw6k=', 'base64').toString('utf8');   // 'café'
Buffer.from('café', 'utf8').toString('base64');       // 'Y2Fmw6k='
Buffer.from(token, 'base64url').toString('utf8');     // Node 16+

Frequently asked questions

Why does my decoded text show "é"?

atob returned bytes that were displayed as Latin-1. Decode them as UTF-8 with TextDecoder.

Is atob deprecated?

No, it remains standard. It simply predates Unicode-aware string APIs.

What is the Node equivalent?

Buffer.from(str, "base64").toString("utf8"), with "base64url" available from Node 16.

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