← Back to Guides

How to Encode and Decode Base64 in JavaScript

· Tags: javascript, base64, btoa, atob, nodejs, encoding, web-development

Base64 Encoding and Decoding in JavaScript

JavaScript provides several built-in ways to encode and decode Base64 strings. Whether you are building a browser application or a Node.js server, understanding how to work with Base64 is essential for handling binary data in text-based formats like JSON, HTML, and URLs.

Using btoa() and atob() in the Browser

The two core functions for Base64 in browser JavaScript are btoa() and atob(). These function names follow an older naming convention — btoa stands for "binary to ASCII" and atob stands for "ASCII to binary." They have been supported in all major browsers for decades.

Encoding with btoa()

const originalString = 'Hello, world!';
const encoded = btoa(originalString);
console.log(encoded);
// Output: SGVsbG8sIHdvcmxkIQ==

Decoding with atob()

const base64String = 'SGVsbG8sIHdvcmxkIQ==';
const decoded = atob(base64String);
console.log(decoded);
// Output: Hello, world!

Both functions work with strings that contain only Latin-1 characters (each character represented by a single byte). This is an important limitation that we will address next.

Handling Unicode and Non-ASCII Characters

A common pitfall: btoa() throws an error when given strings containing characters outside the Latin-1 range, such as emoji, Chinese characters, or accented letters.

btoa('Hello 你好');
// Error: The string to be encoded contains characters outside of the Latin-1 range.

To encode Unicode strings, you need to convert the string to bytes first and then encode those bytes. The modern approach uses the TextEncoder and TextDecoder APIs:

function unicodeToBase64(str) {
  const bytes = new TextEncoder().encode(str);
  const binaryString = String.fromCharCode(...bytes);
  return btoa(binaryString);
}

function base64ToUnicode(base64) {
  const binaryString = atob(base64);
  const bytes = Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

// Usage
const encoded = unicodeToBase64('Hello 你好 🚀');
console.log(encoded);
// Output: SGVsbG8g5L2g5aW9IPCfj4A=

const decoded = base64ToUnicode(encoded);
console.log(decoded);
// Output: Hello 你好 🚀

An older technique uses encodeURIComponent and decodeURIComponent, but the TextEncoder/TextDecoder approach is more robust and is the recommended modern solution.

Encoding Binary Data (ArrayBuffer)

When working with files, images, or raw binary data from APIs like fetch or FileReader, you typically have an ArrayBuffer or Uint8Array. Here is how to convert it to Base64:

function arrayBufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binaryString = '';
  for (let i = 0; i < bytes.length; i++) {
    binaryString += String.fromCharCode(bytes[i]);
  }
  return btoa(binaryString);
}

function base64ToArrayBuffer(base64) {
  const binaryString = atob(base64);
  const bytes = new Uint8Array(binaryString.length);
  for (let i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

Converting a File to Base64 in the Browser

Here is a complete example using FileReader to convert a user-selected file to a Base64 data URL:

function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(new Error('Failed to read file'));
    reader.readAsDataURL(file);
  });
}

// Usage with a file input
document.querySelector('input[type="file"]').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  try {
    const dataUrl = await fileToBase64(file);
    console.log(dataUrl); // data:image/png;base64,iVBORw0KGgo...
  } catch (err) {
    console.error('Conversion failed:', err);
  }
});

Using Base64 in Node.js

Node.js provides the Buffer class for Base64 operations, which is more flexible than the browser's btoa/atob. The Buffer class handles encoding and decoding automatically, including Unicode, and supports multiple encoding formats.

Basic Encoding and Decoding

// Encode a string to Base64
const encoded = Buffer.from('Hello, world!').toString('base64');
console.log(encoded);
// Output: SGVsbG8sIHdvcmxkIQ==

// Decode a Base64 string back to text
const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
console.log(decoded);
// Output: Hello, world!

Unicode Support in Node.js

Unlike btoa() in the browser, Node.js Buffer handles Unicode seamlessly:

const encoded = Buffer.from('Hello 你好 🚀').toString('base64');
console.log(encoded);
// Output: SGVsbG8g5L2g5aW9IPCfj4A=

const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
console.log(decoded);
// Output: Hello 你好 🚀

Reading a File and Encoding as Base64

const fs = require('fs');

// Read a file and encode it to Base64
const buffer = fs.readFileSync('image.png');
const base64 = buffer.toString('base64');
const dataUrl = `data:image/png;base64,${base64}`;

Base64url Encoding in Node.js

Node.js 15.7.0+ supports native Base64url encoding, which replaces + with - and / with _ and omits padding:

const encoded = Buffer.from('Hello, world!').toString('base64url');
console.log(encoded);
// Output: SGVsbG8sIHdvcmxkIQ (no padding)

const decoded = Buffer.from(encoded, 'base64url').toString('utf-8');
console.log(decoded);
// Output: Hello, world!

Performance Considerations

For large data, repeated Base64 operations can be slow. Here are some tips:

  • Use TextEncoder/TextDecoder over manual conversions for Unicode strings
  • In Node.js, Buffer.from() is highly optimized — prefer it over manual implementations
  • Avoid converting large files to Base64 unless necessary (consider streaming)
  • For large datasets, consider using Blob and FileReader in the browser to avoid blocking the main thread

Try Base64 Encoding Online

Experiment with encoding and decoding right in your browser using our free online Base64 encoder and decoder. No coding required — just paste text or upload a file and get the Base64 result instantly.