Base64 Encoding & Decoding in TypeScript

TypeScript doesn't add new Base64 APIs — it runs the same JavaScript under the hood. But types let you build robust, reusable utilities. Here are production-ready, typed Base64 helpers for browser and Node.js.

Browser: Typed btoa / atob Wrappers

// base64.ts — browser-compatible, Unicode-safe

/**
 * Encode a Unicode string to Base64.
 * Uses TextEncoder for correct UTF-8 handling.
 */
export function encodeBase64(input: string): string {
  const bytes = new TextEncoder().encode(input);
  let binary = '';
  bytes.forEach(byte => { binary += String.fromCharCode(byte); });
  return btoa(binary);
}

/**
 * Decode a Base64 string to Unicode text.
 * Returns null if the input is invalid Base64.
 */
export function decodeBase64(input: string): string | null {
  try {
    const binary = atob(input);
    const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
    return new TextDecoder().decode(bytes);
  } catch {
    return null;
  }
}

/**
 * Encode a string to URL-safe Base64 (no padding).
 */
export function encodeBase64URL(input: string): string {
  return encodeBase64(input)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

/**
 * Decode a URL-safe Base64 string.
 */
export function decodeBase64URL(input: string): string | null {
  let base64 = input.replace(/-/g, '+').replace(/_/g, '/');
  while (base64.length % 4) base64 += '=';
  return decodeBase64(base64);
}

Node.js: Buffer-Based Utilities

// base64-node.ts — Node.js only

import { Buffer } from 'buffer';

export function encodeBase64(input: string | Buffer): string {
  const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : input;
  return buf.toString('base64');
}

export function decodeBase64(input: string): string {
  return Buffer.from(input, 'base64').toString('utf8');
}

export function encodeBase64URL(input: string | Buffer): string {
  const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : input;
  return buf.toString('base64url'); // Node 16+
}

export function decodeBase64URL(input: string): Buffer {
  return Buffer.from(input, 'base64url'); // Node 16+
}

// Encode a file
import { readFileSync, writeFileSync } from 'fs';

export function encodeFile(path: string): string {
  return readFileSync(path).toString('base64');
}

export function decodeToFile(encoded: string, outputPath: string): void {
  writeFileSync(outputPath, Buffer.from(encoded, 'base64'));
}

Universal (Browser + Node.js)

For libraries or packages that must work in both environments:

// base64-universal.ts

function isNode(): boolean {
  return typeof process !== 'undefined' && !!process.versions?.node;
}

export function encodeBase64(input: string): string {
  if (isNode()) {
    return Buffer.from(input, 'utf8').toString('base64');
  }
  // Browser path
  const bytes = new TextEncoder().encode(input);
  let binary = '';
  bytes.forEach(b => { binary += String.fromCharCode(b); });
  return btoa(binary);
}

export function decodeBase64(input: string): string {
  if (isNode()) {
    return Buffer.from(input, 'base64').toString('utf8');
  }
  const binary = atob(input);
  const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

Typed Data URI Helper

interface DataURI {
  mimeType: string;
  encoding: 'base64';
  data: string;
  toString(): string;
}

export function parseDataURI(uri: string): DataURI | null {
  const match = uri.match(/^data:([^;]+);base64,(.+)$/);
  if (!match) return null;
  return {
    mimeType: match[1],
    encoding: 'base64',
    data: match[2],
    toString: () => uri,
  };
}

export function buildDataURI(mimeType: string, base64Data: string): string {
  return `data:${mimeType};base64,${base64Data}`;
}

Zod Validation for Base64 Fields

import { z } from 'zod';

const Base64Schema = z.string().refine(
  (s) => {
    try { atob(s); return true; } catch { return false; }
  },
  { message: 'Invalid Base64 string' }
);

// Usage in API schema
const UploadSchema = z.object({
  filename: z.string(),
  content: Base64Schema,
  mimeType: z.string(),
});

Test your TypeScript Base64 output

Paste encoded strings from your TypeScript code into base64.dev to inspect and decode them.

Open base64.dev →