Working with Base64 in JavaScript

Learn how to encode and decode Base64 strings in both the browser using btoa() and atob(), and on the server with Node.js Buffer.

JavaScript handles Base64 differently depending on where your code is running. If you are writing code for the browser (frontend), you will use the built-in Window methods btoa() and atob(). If you are writing backend code in Node.js, you will use the powerful Buffer API.

In this guide, we'll cover both environments and how to handle tricky edge cases like Unicode (UTF-8) characters.

1. Browser: Encoding Strings with btoa()

In the browser, btoa() (which stands for "binary to ASCII") converts a string into a Base64-encoded string.

javascript
const text = "Hello, JavaScript Base64!";

// Encode the string
const encodedData = btoa(text);

console.log(encodedData);
// Output: "SGVsbG8sIEphdmFTY3JpcHQgQmFzZTY0IQ=="

2. Browser: Decoding Strings with atob()

Conversely, atob() ("ASCII to binary") decodes a Base64 string back into human-readable text.

javascript
const encodedData = "SGVsbG8sIEphdmFTY3JpcHQgQmFzZTY0IQ==";

// Decode the Base64 string
const decodedText = atob(encodedData);

console.log(decodedText);
// Output: "Hello, JavaScript Base64!"

The Unicode (UTF-8) Problem

btoa() expects the input string to be strictly 8-bit characters (ASCII). If you try to pass it a Unicode string containing emojis or special characters (like "๐Ÿ‘‹ Hello" or "ใ“ใ‚“ใซใกใฏ"), the browser will throw a DOMException: String contains an invalid character error.

How to fix the Unicode error

To safely encode strings with emojis or special characters in the browser, you must encode the text into UTF-8 bytes first. You can do this easily using the TextEncoder and TextDecoder APIs, combined with modern JavaScript typed arrays.

javascript
// --- SAFE UNICODE ENCODING ---
function encodeBase64Unicode(text) {
  const bytes = new TextEncoder().encode(text);
  const binString = Array.from(bytes, (byte) =>
    String.fromCodePoint(byte),
  ).join("");
  return btoa(binString);
}

// --- SAFE UNICODE DECODING ---
function decodeBase64Unicode(base64) {
  const binString = atob(base64);
  const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0));
  return new TextDecoder().decode(bytes);
}

// Test it with Emojis!
const originalText = "Hello ๐Ÿ‘‹ ๐ŸŒ";

const safeEncoded = encodeBase64Unicode(originalText);
console.log(safeEncoded); // Output: "SGVsbG8g8J+RiyDwn42N"

const safeDecoded = decodeBase64Unicode(safeEncoded);
console.log(safeDecoded); // Output: "Hello ๐Ÿ‘‹ ๐ŸŒ"

3. Node.js: Encoding and Decoding with Buffer

If you are running JavaScript on the backend (Node.js, Express, Next.js API Routes), you shouldn't use btoa() or atob(). Instead, you should use the globally available Buffer class, which natively supports UTF-8 and Base64 without any extra boilerplate.

javascript
const text = "Hello Node.js! ๐Ÿ‘‹";

// --- ENCODING in Node.js ---
// 1. Create a buffer from the UTF-8 string
const buffer = Buffer.from(text, "utf-8");

// 2. Convert the buffer to a Base64 string
const base64String = buffer.toString("base64");

console.log(base64String); 
// Output: "SGVsbG8gTm9kZS5qcyEg8J+Riw=="


// --- DECODING in Node.js ---
// 1. Create a buffer from the Base64 string
const decodedBuffer = Buffer.from(base64String, "base64");

// 2. Convert the buffer back to a UTF-8 string
const decodedText = decodedBuffer.toString("utf-8");

console.log(decodedText); 
// Output: "Hello Node.js! ๐Ÿ‘‹"

4. Node.js: Base64URL Encoding

When building APIs, you often need to pass Base64 data inside a URL (like in a JSON Web Token). Node.js Buffer natively supports the base64url format, which automatically replaces the unsafe + and / characters, and strips the = padding.

javascript
const secretData = 'subjects?dir=/usr/local&topic=crypto';

// Standard Base64 (Bad for URLs - contains '/' and '=')
const standard = Buffer.from(secretData).toString('base64');
console.log(standard);
// Output: c3ViamVjdHM/ZGlyPS91c3IvbG9jYWwmdG9waWM9Y3J5cHRv

// Base64URL (Safe for URLs)
const urlSafe = Buffer.from(secretData).toString('base64url');
console.log(urlSafe);
// Output: c3ViamVjdHM_ZGlyPS91c3IvbG9jYWwmdG9waWM9Y3J5cHRv

Need a quick conversion?

Skip the code and use our interactive web tool to instantly encode or decode Base64 strings, including full support for Unicode and Base64URL formats.

Go to the Base64 Converter Tool