TL;DR: Use atob() in browsers, Buffer.from(str, "base64") in Node.js, or base64.b64decode() in Python. Watch out for Unicode text — plain atob() can't handle it, so use TextDecoder instead.
Just Give Me the Code
Sometimes you don't want the backstory — you just want to decode a Base64 string and move on with your life. I respect that. Here's a cheat sheet for every popular language:
JavaScript (Browser)
// Simple ASCII decoding
const decoded = atob("SGVsbG8gV29ybGQ=");
console.log(decoded); // "Hello World"
// For Unicode strings (emojis, accented chars, etc.)
function decodeBase64Unicode(str) {
const bytes = atob(str);
const uint8 = new Uint8Array(bytes.length);
for (let i = 0; i < bytes.length; i++) {
uint8[i] = bytes.charCodeAt(i);
}
return new TextDecoder().decode(uint8);
}
Node.js
const decoded = Buffer.from("SGVsbG8gV29ybGQ=", "base64").toString("utf-8");
// "Hello World"
// URL-safe Base64
const urlSafe = Buffer.from("SGVsbG8_V29ybGQ", "base64url").toString("utf-8");
Python
import base64
decoded = base64.b64decode("SGVsbG8gV29ybGQ=").decode("utf-8")
# "Hello World"
Command Line
# macOS
echo "SGVsbG8gV29ybGQ=" | base64 -D
# Linux
echo "SGVsbG8gV29ybGQ=" | base64 -d
Dev Joke: I told my friend I could decode Base64 in my head. He said "dGhhdCdzIG5vdCBpbXByZXNzaXZl". I said, "that's not impressive." Wait...
The Unicode Trap with atob()
Here's where things get spicy. The browser's atob() function has a well-known limitation: it only handles ASCII text. If the original data had emoji, accented characters, or any non-English text, atob() will either produce garbage or throw an error.
Why? Because atob() treats each byte as one character, but Unicode characters can be multiple bytes. It's like trying to read a Chinese novel one letter at a time — the individual pieces don't make sense without the full picture.
The fix is to decode the bytes first, then interpret them as UTF-8:
function base64ToText(base64) {
const binaryString = atob(base64);
const bytes = Uint8Array.from(binaryString, c => c.charCodeAt(0));
return new TextDecoder("utf-8").decode(bytes);
}
// Now emojis work!
base64ToText("8J+agCBIZWxsbyBXb3JsZA==");
// "🚀 Hello World"
Rule of thumb: If you're only dealing with English text and numbers, atob() is fine. The moment you have any chance of emoji, accented letters, or non-Latin characters, use the TextDecoder approach.
Decoding Base64 Images
Getting a Base64-encoded image from an API? Here's how to handle it:
Display in the Browser
// If you have a full data URL, just set it directly
const dataUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...";
document.getElementById("myImage").src = dataUrl;
// If you only have the raw Base64 string
const base64 = "iVBORw0KGgoAAAANSUhEUg...";
const img = document.createElement("img");
img.src = `data:image/png;base64,${base64}`;
document.body.appendChild(img);
Save to File in Node.js
const fs = require("fs");
const buffer = Buffer.from(base64Image, "base64");
fs.writeFileSync("output.png", buffer);
Convert to Blob for Uploading
function base64ToBlob(base64, mimeType) {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return new Blob([bytes], { type: mimeType });
}
const blob = base64ToBlob(imageData, "image/png");
const formData = new FormData();
formData.append("file", blob, "photo.png");
fetch("/upload", { method: "POST", body: formData });
Dealing with URL-Safe Base64
You'll run into URL-safe Base64 a lot, especially in JWTs and query parameters. It swaps + for - and / for _, and often drops the = padding. The browser's atob() doesn't handle this natively, so you need to convert first:
function decodeUrlSafeBase64(str) {
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
const padding = base64.length % 4;
if (padding === 2) base64 += "==";
else if (padding === 3) base64 += "=";
return atob(base64);
}
Is This String Even Base64?
There's no foolproof way to detect Base64 because any ASCII string could theoretically be valid. But you can do a reasonable check:
function isLikelyBase64(str) {
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(str)) return false;
if (str.length % 4 !== 0) return false;
try { atob(str); return true; }
catch { return false; }
}
Common Errors and Quick Fixes
- "Failed to execute 'atob'" — Your input has characters outside the Base64 alphabet. Check for whitespace, newlines, or URL-safe characters that need converting.
- Garbled output — Probably Unicode. Switch to the
TextDecoderapproach. - Missing padding — Some encoders skip the trailing
=signs. Add them back: iflength % 4is 2, add==; if it's 3, add=.
Watch out: When decoding binary data (images, PDFs), never treat the result as a string at any point. Use Uint8Array or Buffer throughout the entire pipeline, or you'll get corrupted files.
Try It Yourself
Paste any Base64 string into our decoder to instantly see the original text. Supports standard and URL-safe variants, handles Unicode correctly, and runs entirely in your browser.
Open Base64 Decoder →