How to Base64 Encode and Decode in JavaScript

JavaScript has built-in Base64 functions, but they have a famous UTF-8 trap. Here's how to encode and decode correctly.

The basics: btoa and atob

btoa('Hello');   // "SGVsbG8="  (encode)
atob('SGVsbG8=');  // "Hello"    (decode)

These work for plain ASCII. The names come from "binary to ASCII" and "ASCII to binary."

The UTF-8 problem

btoa throws on characters outside Latin-1 (like emoji or many accented letters):

btoa('café');   // ❌ throws or corrupts

The UTF-8-safe way

Encode through TextEncoder first:

function toBase64(str) {
  const bytes = new TextEncoder().encode(str);
  let bin = '';
  bytes.forEach(b => bin += String.fromCharCode(b));
  return btoa(bin);
}

function fromBase64(b64) {
  const bin = atob(b64);
  const bytes = Uint8Array.from(bin, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

This round-trips emoji, accents, and any Unicode correctly.

In Node.js

Node uses Buffer, which handles UTF-8 natively:

Buffer.from('café').toString('base64');        // encode
Buffer.from(b64, 'base64').toString('utf-8');  // decode

URL-safe Base64

For tokens and URLs, swap the characters and strip padding:

b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

More on that in URL-safe Base64 encoding.

Test it

Verify your output against the Base64 tool — paste text to encode or a string to decode and compare.

Got a config file to check?

Open the config toolkit →