Serverless-first. Built for cold starts, tight memory, and one binary.Deploy guide →

Text encoding

UTF-8 only. Both constructors ignore their arguments — there is no latin1, utf-16, or fatal mode.

const enc = new TextEncoder();
const dec = new TextDecoder();

const bytes = enc.encode("héllo");        // Uint8Array
const text = dec.decode(bytes);           // "héllo"

TextEncoder

MemberTypeDescription
encodingstringAlways "utf-8"
encode(input)(string) → Uint8ArrayUTF-8 bytes
const enc = new TextEncoder();
enc.encoding;                     // "utf-8"
[...enc.encode("hi")];            // [104, 105]
[...enc.encode("")];              // []

There is no encodeInto — write into a target buffer yourself:

function encodeInto(str) {                 // poor man's encodeInto
  const bytes = new TextEncoder().encode(str);
  return { read: str.length, written: bytes.length, bytes };
}

TextDecoder

MemberTypeDescription
encodingstringAlways "utf-8"
decode(input?)(ArrayBuffer | TypedArray) → stringUTF-8 → string
const dec = new TextDecoder();
dec.encoding;                          // "utf-8"
dec.decode();                          // ""  (no input)
dec.decode(enc.encode("ok"));          // "ok"
dec.decode(new Uint8Array([0xff, 0x41]));  // "\uFFFDA"  — never throws

Invalid bytes are replaced, never fatal

Malformed UTF-8 becomes U+FFFD (``) — byte by byte:

dec.decode(new Uint8Array([0x41, 0xff, 0x42]));
// "A\uFFFDB"

There is no fatal: true mode: new TextDecoder("utf-8", { fatal: true }) parses but the options are ignored.

Wrong input type throws

dec.decode("not bytes");
// TypeError: TextDecoder.decode requires ArrayBuffer or TypedArray input

Strings are rejected — convert with encode() first. ArrayBuffer itself is accepted:

dec.decode(enc.encode("ok").buffer);   // "ok"

Constructor labels are ignored

new TextDecoder("latin1").encoding;    // "utf-8"  — silently ignored

Patterns

JSON over bytes

const res = await fetch("https://example.com/api");
const data = JSON.parse(new TextDecoder().decode(await res.arrayBuffer()));

Base64 → bytes → string

const bin = atob(base64);
const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
console.log(new TextDecoder().decode(bytes));

Gotchas

  • Both classes are UTF-8 only; labels don't change behaviour.
  • decode() is lossy by design — invalid sequences become ``. If you need to detect corruption, validate the bytes yourself (TextDecoder won't tell you).
  • encode() allocates a fresh Uint8Array per call — don't call it in a hot loop with large strings.
  • No streaming: decode(chunk, { stream: true }) ignores the option and may mangle multi-byte characters split across chunks.

See also