atob, btoa, base64-js, and js-base64 are utilities for handling Base64 transformations in JavaScript environments. atob and btoa are typically polyfills for the native browser globals, offering minimal functionality for ASCII-only strings. base64-js provides a robust, isomorphic implementation focused on converting between binary data (Uint8Array) and Base64 strings, making it ideal for Node.js and modern browser APIs. js-base64 is a comprehensive library that handles UTF-8 encoding correctly out of the box, solving common internationalization issues that native functions struggle with. Choosing the right tool depends on whether you need raw binary handling, UTF-8 safety, or simple ASCII compatibility.
Handling Base64 data is a routine task in web development, whether you are uploading images, managing authentication tokens, or transferring binary payloads. However, the JavaScript ecosystem offers several approaches, each with distinct trade-offs regarding character encoding, environment support, and API ergonomics. The packages atob, btoa, base64-js, and js-base64 represent the spectrum from native polyfills to robust isomorphic libraries. Let's examine how they differ in real-world engineering scenarios.
atob and btoa are designed to mimic the native browser globals window.atob and window.btoa.
// atob: Decodes ASCII Base64
import atob from 'atob';
const decoded = atob('SGVsbG8gV29ybGQ=');
// Output: "Hello World"
// btoa: Encodes ASCII string
import btoa from 'btoa';
const encoded = btoa('Hello World');
// Output: "SGVsbG8gV29ybGQ="
base64-js focuses on binary data conversion.
Uint8Array (or Node Buffer) and Base64 strings.// base64-js: Binary to Base64
import * as base64 from 'base64-js';
const bytes = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
const encoded = base64.fromByteArray(bytes);
// Output: "SGVsbG8="
js-base64 is a full-featured string utility.
// js-base64: UTF-8 String to Base64
import { Base64 } from 'js-base64';
const encoded = Base64.encode('Hello 世界');
// Output: Correctly handles Unicode without manual escaping
One of the most common pitfalls in Base64 handling is Unicode support. Native btoa throws errors on non-ASCII characters.
atob / btoa require manual workarounds for Unicode.
btoa.// btoa: Unicode workaround required
import btoa from 'btoa';
const str = 'Hello 世界';
// Direct call throws error: btoa('Hello 世界') ❌
const encoded = btoa(unescape(encodeURIComponent(str)));
// Requires manual encoding pipeline
base64-js expects binary input.
TextEncoder to convert strings to bytes first.// base64-js: Explicit TextEncoder usage
import * as base64 from 'base64-js';
const str = 'Hello 世界';
const bytes = new TextEncoder().encode(str);
const encoded = base64.fromByteArray(bytes);
// Clean separation of text encoding and base64 encoding
js-base64 handles UTF-8 automatically.
// js-base64: Automatic UTF-8 handling
import { Base64 } from 'js-base64';
const str = 'Hello 世界';
const encoded = Base64.encode(str);
// No extra steps needed for Unicode
Deployment targets dictate which library fits best.
atob / btoa are primarily for browsers.
atob/btoa exist in newer versions, but these packages act as polyfills.Buffer objects.// atob/btoa in Node
// Works but treats data as binary strings, not Buffers
import btoa from 'btoa';
// Not ideal for Node stream processing
base64-js is truly isomorphic.
Buffer and browser Uint8Array.// base64-js: Node Buffer support
import * as base64 from 'base64-js';
const buffer = Buffer.from('Hello');
const encoded = base64.fromByteArray(buffer);
// Works identically in Node and Browser
js-base64 is also isomorphic but string-focused.
// js-base64: Isomorphic string handling
import { Base64 } from 'js-base64';
// Works in Node, Browser, Deno, etc.
const decoded = Base64.decode(encodedString);
Base64 strings often need to be passed in URLs or headers, requiring URL-safe variants (replacing + with - and / with _).
atob / btoa have no URL-safe support.
// btoa: Manual URL-safe conversion
import btoa from 'btoa';
let encoded = btoa(data);
encoded = encoded.replace(/\+/g, '-').replace(/\//g, '_');
// Manual post-processing required
base64-js focuses on standard Base64.
// base64-js: Standard output
import * as base64 from 'base64-js';
let encoded = base64.fromByteArray(bytes);
// Must manually replace characters for URL safety
js-base64 includes URL-safe methods.
encodeURI and decodeURI methods specifically for this.// js-base64: Built-in URL-safe methods
import { Base64 } from 'js-base64';
const encoded = Base64.encodeURI(data);
// Automatically handles + and / replacement
| Feature | atob / btoa | base64-js | js-base64 |
|---|---|---|---|
| Primary Use | Native Polyfill | Binary Conversion | UTF-8 String Handling |
| Unicode Support | ❌ (Manual workaround) | ✅ (Via TextEncoder) | ✅ (Automatic) |
| Input Type | ASCII String | Uint8Array / Buffer | UTF-8 String |
| URL-Safe | ❌ (Manual) | ❌ (Manual) | ✅ (Built-in) |
| Environment | Browser / Node Polyfill | Isomorphic | Isomorphic |
| Maintenance | Legacy / Polyfill | Active | Active |
atob and btoa are legacy tools 🕰️. Use them only if you are maintaining old codebases that rely on these specific polyfills or if you are targeting extremely old environments without native support. For any new development, avoid them due to their poor Unicode handling.
base64-js is the engineer's choice for binary data 🔧. If you are working with Blob, File, Buffer, or Uint8Array, this is the most performant and correct option. It forces you to be explicit about text encoding, which prevents subtle bugs in data pipelines.
js-base64 is the developer's choice for text strings 📝. If your primary use case is encoding user input, JWT tokens, or configuration strings that may contain Unicode, this library saves you from writing encoding boilerplate. It is the safest default for general-purpose application logic.
Final Thought: Modern JavaScript provides native TextEncoder and TextDecoder APIs. For maximum control and minimal dependencies, pairing native text encoding with base64-js is often the most robust architectural decision. However, for speed of development and UTF-8 safety without boilerplate, js-base64 remains a strong contender.
Choose atob only if you need a lightweight polyfill for the native atob() function in older environments where it is missing. It is not suitable for modern UTF-8 text handling and should be avoided if you need to decode non-ASCII characters safely. Use this strictly for legacy support where native APIs are absent.
Choose base64-js when you need a fast, isomorphic library to convert between Uint8Array (binary data) and Base64 strings. It is the standard choice for Node.js buffers and modern browser Blob handling where performance and binary accuracy matter. It does not handle text encoding automatically, so pair it with TextEncoder for string data.
Choose btoa only as a polyfill for the native btoa() function in environments lacking it. Like atob, it handles binary strings and fails on Unicode characters without manual encoding workarounds. It is best reserved for legacy compatibility layers rather than new feature development.
Choose js-base64 when you need to encode or decode UTF-8 strings directly without managing binary buffers manually. It is the safest option for user-generated content, international text, and full-stack applications where character encoding issues are a risk. It abstracts away the complexity of unescape(encodeURIComponent()) workarounds.
| atob | btoa | unibabel.js | Sponsored by ppl
Uses Buffer to emulate the exact functionality of the browser's atob.
Note: Unicode may be handled incorrectly (like the browser).
It turns base64-encoded ascii data back to binary.
(function () {
"use strict";
var atob = require('atob');
var b64 = "SGVsbG8sIFdvcmxkIQ==";
var bin = atob(b64);
console.log(bin); // "Hello, World!"
}());
Check out unibabel.js
Code copyright 2012-2018 AJ ONeal
Dual-licensed MIT and Apache-2.0
Docs copyright 2012-2018 AJ ONeal
Docs released under Creative Commons.