Base64 Encode & Decode in JavaScript
Using btoa(), atob(), and Buffer in the browser and Node.js — including Unicode handling and common pitfalls.
Browser: btoa() and atob()
Modern browsers have two built-in functions for Base64:
btoa(str)— binary to ASCII — encodes to Base64atob(str)— ASCII to binary — decodes from Base64
// Encode
const encoded = btoa("Hello, World!");
console.log(encoded); // SGVsbG8sIFdvcmxkIQ==
// Decode
const decoded = atob("SGVsbG8sIFdvcmxkIQ==");
console.log(decoded); // Hello, World!The Unicode Problem with btoa()
btoa() only handles Latin1 characters (byte values 0–255). Passing a string with emoji or multi-byte Unicode characters throws InvalidCharacterError:
// ❌ Throws error
btoa("Hello 🌍"); // InvalidCharacterError
// ✅ Fix: encode URI components first
btoa(unescape(encodeURIComponent("Hello 🌍")));
// → "SGVsbG8g8J+MjQ=="
// ✅ Decode back
decodeURIComponent(escape(atob("SGVsbG8g8J+MjQ==")));
// → "Hello 🌍"Node.js: Buffer (Recommended)
In Node.js, Buffer handles Unicode natively and is the preferred approach:
// Encode
const encoded = Buffer.from("Hello 🌍", "utf8").toString("base64");
console.log(encoded); // SGVsbG8g8J+MjQ==
// Decode
const decoded = Buffer.from("SGVsbG8g8J+MjQ==", "base64").toString("utf8");
console.log(decoded); // Hello 🌍
// Encode a file
const fs = require("fs");
const fileBuffer = fs.readFileSync("./image.png");
const base64Image = fileBuffer.toString("base64");
// Decode back to file
fs.writeFileSync("./output.png", Buffer.from(base64Image, "base64"));URL-Safe Base64 in JavaScript
Standard Base64 uses + and / which must be percent-encoded in URLs. Convert to URL-safe format:
// Standard → URL-safe
const urlSafe = btoa("Hello+World").replace(/+/g, "-").replace(///g, "_").replace(/=/g, "");
// URL-safe → Standard (for decoding)
function fromUrlSafe(str) {
return str.replace(/-/g, "+").replace(/_/g, "/") + "==".slice((str.length % 4) || 4);
}
const decoded = atob(fromUrlSafe(urlSafe));
// Node.js URL-safe encode
const urlSafeNode = Buffer.from("Hello+World").toString("base64url");
// → "SGVsbG8rV29ybGQ" (no padding, - and _ instead of + and /)Decoding a JWT Payload
function decodeJwtPayload(token) {
const payload = token.split(".")[1]; // Get middle section
// Convert URL-safe Base64 to standard
const standard = payload.replace(/-/g, "+").replace(/_/g, "/");
// Decode and parse JSON
return JSON.parse(atob(standard));
}
const jwt = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoiYWRtaW4ifQ.xxx";
console.log(decodeJwtPayload(jwt));
// → { user: "alice", role: "admin" }Quick Reference
| Environment | Encode | Decode |
|---|---|---|
| Browser (ASCII only) | btoa(str) | atob(str) |
| Browser (Unicode) | btoa(unescape(encodeURIComponent(str))) | decodeURIComponent(escape(atob(str))) |
| Node.js | Buffer.from(str).toString('base64') | Buffer.from(b64, 'base64').toString('utf8') |
| Node.js URL-safe | Buffer.from(str).toString('base64url') | Buffer.from(b64, 'base64url').toString('utf8') |
Test Base64 online
Encode or decode any Base64 string instantly in your browser — no code required.
Open Base64 Converter →Frequently Asked Questions
How do I encode a string to Base64 in JavaScript?
Use btoa() in the browser: const encoded = btoa('Hello'); // → SGVsbG8=. In Node.js, use Buffer.from('Hello').toString('base64').
Why does btoa() throw an error with Unicode characters?
btoa() only handles Latin1 characters (code points 0-255). For Unicode strings, use: btoa(unescape(encodeURIComponent(str))). In Node.js, use Buffer.from(str, 'utf8').toString('base64') which handles Unicode natively.
How do I decode Base64 in JavaScript?
Use atob() in the browser: const decoded = atob('SGVsbG8='); // → Hello. In Node.js, use Buffer.from('SGVsbG8=', 'base64').toString('utf8').
Continue reading: