Base64 encoding

btoa() and atob(), with TextEncoder for text

Baseline widely available
  • Chrome38
  • Edge79
  • Firefox19
  • Safari10.1

Features it needs

btoa() and atob() have been in browsers since the beginning, and the reason people reach for a library instead is that btoa() throws on any character above U+00FF. Encoding through TextEncoder first fixes that: turn the string into UTF-8 bytes, map them to a binary string, then encode. That is four lines, and it is exactly what the libraries do.

When this applies

Encoding or decoding base64 in the browser.

The native approach

function encode(text) {
  const bytes = new TextEncoder().encode(text);
  const binary = String.fromCharCode(...bytes);
  return btoa(binary);
}

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

encode("caffè ☕"); // "Y2FmZsOoIOKYlQ=="

MDN reference

When the dependency is still right

An answer that always says "the platform covers it" is worse than no answer. These are the cases where this one does not hold.

  • You encode large binary payloads. String.fromCharCode(...bytes) spreads the whole array onto the stack and blows up past a few hundred thousand bytes, so a chunked loop is needed and the library already has one.
  • You need URL-safe base64 with the - and _ alphabet and no padding. btoa does not produce it, so the substitution is yours to write on both sides.
  • You want the encoding to be synchronous and allocation-light on a hot path, where a library tuned for it can beat the two-step conversion.
  • You are on a runtime where Buffer is available and simpler, such as server code that never runs in a browser.
  • You rely on the library accepting input types beyond a string, for example ArrayBuffers or streams.

Packages this covers