Base64 Encode & Decode in JavaScript

Encode or decode below — it runs locally in your browser — then grab the canonical JavaScript code. The built-in btoa() and atob() only handle Latin-1, so Unicode text needs TextEncoder / TextDecoder first.

Last updated

INPUT
OUTPUT
Type or paste to encode / decode

Encode and decode a string

For plain ASCII, btoa() encodes and atob() decodes:

// Encode
const encoded = btoa("Hello, world!");
console.log(encoded); // "SGVsbG8sIHdvcmxkIQ=="

// Decode
const decoded = atob("SGVsbG8sIHdvcmxkIQ==");
console.log(decoded); // "Hello, world!"

For Unicode (emoji, accents, non-Latin scripts), encode to UTF-8 bytes first, then convert to a binary string for btoa():

// Unicode-safe encode
function encodeBase64(str) {
  const bytes = new TextEncoder().encode(str);
  const binary = String.fromCharCode(...bytes);
  return btoa(binary);
}

// Unicode-safe decode
function decodeBase64(base64) {
  const binary = atob(base64);
  const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

encodeBase64("Hello 🌍"); // "SGVsbG8g8J+MjQ=="

Tip: btoa() only accepts Latin-1 (code points 0–255). Passing emoji or accented text raises InvalidCharacterError — always run Unicode strings through TextEncoder first.

The modern way: Uint8Array.toBase64()

As of 2025 (ES2026), browsers and Node.js 22+ ship native Uint8Array.prototype.toBase64() and Uint8Array.fromBase64(). They work directly on bytes, so Unicode "just works" with no String.fromCharCode dance:

// Encode: string -> bytes -> Base64
const bytes = new TextEncoder().encode("Hello 🌍");
bytes.toBase64();                          // "SGVsbG8g8J+MjQ=="
bytes.toBase64({ alphabet: "base64url", omitPadding: true }); // URL-safe

// Decode: Base64 -> bytes -> string
const decoded = Uint8Array.fromBase64("SGVsbG8g8J+MjQ==");
new TextDecoder().decode(decoded);         // "Hello 🌍"

Support: shipped in Chrome, Firefox, Safari, Node 22+, Deno, and Bun. For older runtimes, fall back to the TextEncoder + btoa() pattern above.

URL-safe Base64

For JWTs, URLs, and filenames, swap + and / for - and _ and drop the padding:

// Encode to URL-safe Base64
function toBase64URL(str) {
  return encodeBase64(str)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, ""); // remove padding
}

// Decode URL-safe Base64
function fromBase64URL(base64url) {
  let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
  while (base64.length % 4) base64 += "="; // restore padding
  return decodeBase64(base64);
}

Encode a file

In the browser, use the FileReader API to read a file as a Base64 data URI:

function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result); // data:mime;base64,...
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// Usage with a file input
input.addEventListener("change", async (e) => {
  const dataUri = await fileToBase64(e.target.files[0]);
  console.log(dataUri); // "data:image/png;base64,iVBOR..."
});

Base64 in Node.js

On the server there's no btoa() needed — the Buffer class is the idiomatic API and handles Unicode automatically:

// Encode / decode a string
const encoded = Buffer.from("Hello, world! 🌍").toString("base64");
const decoded = Buffer.from(encoded, "base64").toString("utf8");

// URL-safe Base64 (Node 16+)
Buffer.from("Hello").toString("base64url");

// Encode a file
const fs = require("fs");
const b64 = fs.readFileSync("image.png").toString("base64");
fs.writeFileSync("copy.png", Buffer.from(b64, "base64"));

See the Node.js tool page for a runnable version, or the Node.js guide for streams and large files.

Frequently asked questions

How do I Base64 encode a string in JavaScript?

Use btoa("Hello") for ASCII, which returns "SGVsbG8=". For Unicode, encode to UTF-8 with new TextEncoder().encode(str) first, then btoa() the binary string.

How do I Base64 decode in JavaScript?

atob("SGVsbG8=") returns the binary string "Hello"; for Unicode, pass the bytes through new TextDecoder().decode().

Why does btoa() throw an InvalidCharacterError?

btoa() only accepts Latin-1 characters (0–255). Emoji and non-Latin text throw — encode to UTF-8 bytes with TextEncoder first.

How do I do URL-safe Base64?

Apply .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") to the standard output, then reverse it before atob(). Or in modern runtimes, bytes.toBase64({ alphabet: "base64url" }).

How do I Base64 encode in Node.js?

Use Buffer: Buffer.from("Hello").toString("base64") to encode and Buffer.from(b64, "base64").toString("utf8") to decode. It handles Unicode automatically and supports "base64url" for URL-safe output.

Is btoa() deprecated?

No — btoa()/atob() are not deprecated and still work everywhere. But the newer Uint8Array.toBase64()/fromBase64() methods are the recommended modern approach: they operate on bytes, so they handle Unicode and URL-safe alphabets without workarounds.

Need image, file, or URL-safe modes?

The main base64.dev tool handles text, images, files, and URL-safe Base64 with auto-detect.

Open base64.dev →