jsguides

TextDecoder

new TextDecoder(encoding)

TextDecoder turns raw bytes into a JavaScript string. It is part of the Encoding API — the counterpart to TextEncoder. In browsers it lives on window; it also works in Web Workers. Every modern JavaScript environment supports it.

Constructor

new TextDecoder(encoding = "utf-8", options)

Parameters

  • encoding (string) — A valid WHATWG encoding label. Defaults to "utf-8". Case-insensitive, so "UTF-8" and "utf-8" work the same.
  • options (object) — Optional configuration:
    • fatal (boolean) — When true, decode() throws a TypeError on malformed bytes. Defaults to false (replaces bad bytes with the Unicode replacement character U+FFFD).
    • ignoreBOM (boolean) — When true, skips the byte order mark. Defaults to false.

Throws

  • RangeError if the encoding label is not a recognized WHATWG label.
// Default UTF-8 decoder
const decoder = new TextDecoder();

// Decoder for a specific encoding
const latinDecoder = new TextDecoder("iso-8859-1");

// Strict decoder — throws on invalid bytes
const strictDecoder = new TextDecoder("utf-8", { fatal: true });

encoding

dec.encoding  // "utf-8"

A read-only property that returns the name of the encoding the decoder is using. Note that encoding labels are normalized — creating a decoder with "UTF-8" still reports .encoding as "utf-8".

const dec = new TextDecoder("ISO-8859-1");
console.log(dec.encoding); // "windows-1252"

decode()

decode(buffer, options)

Parameters

  • buffer (ArrayBuffer | TypedArray | DataView) — The encoded byte data to decode.
  • options (object) — Optional:
    • stream (boolean) — Set true when decoding a chunk of a larger stream and more data will follow. Omit or set false for the final chunk (or a complete buffer in one call).

Returns string — The decoded text.

Throws

  • TypeError if fatal: true was set and the buffer contains invalid data for the given encoding.
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const text = new TextDecoder().decode(bytes);
console.log(text); // "Hello"

Fatal mode

With fatal: true, malformed bytes cause an immediate error instead of silent substitution:

const bad = new Uint8Array([0x80]); // not valid UTF-8

// Default: replacement character inserted
const result = new TextDecoder().decode(bad);
console.log(result.length); // 1 — one U+FFFD character

// Strict: throws
try {
  new TextDecoder("utf-8", { fatal: true }).decode(bad);
} catch (e) {
  console.log(e instanceof TypeError); // true
}

Streaming decode

When processing data in chunks (for example, from a file or network stream), pass stream: true on all chunks except the last. This tells the decoder to hold state between calls instead of flushing after each chunk:

const chunks = [
  new Uint8Array([0x48, 0x69]),        // "Hi"
  new Uint8Array([0x20, 0x74, 0x68]),  // " th"
  new Uint8Array([0x65, 0x72, 0x65]),  // "ere"
];

const decoder = new TextDecoder();
let result = "";

for (const chunk of chunks) {
  result += decoder.decode(chunk, { stream: true });
}
result += decoder.decode(); // final chunk — no buffer, stream: false by default

console.log(result); // "Hi there"

A common mistake is using stream: true on the final chunk. When you do that, the decoder waits for more data and returns an empty string. Always call decode() with no arguments (or stream: false) to flush the final chunk.

Common Gotchas

Default fatal: false silently replaces bad bytes. If you need strict validation, you must pass { fatal: true } to the constructor. Most developers never notice corrupted data because the replacement character looks like a normal character in casual inspection.

Encoding labels are normalized. The WHATWG spec maps many label aliases to canonical names. "utf-8" and "UTF-8" both produce a decoder with .encoding === "utf-8". Some legacy labels map to different encodings — "iso-8859-1" actually maps to "windows-1252".

BOM is always skipped. The UTF-8 byte order mark (0xEF 0xBB 0xBF) does not appear in the output regardless of how you set ignoreBOM. There is no option to include it.

stream: true on the final chunk returns an empty string. This trips up developers processing a fixed set of chunks — the last chunk should use decode() with no options or stream: false.

See Also

  • TextEncoder — Encodes strings to UTF-8 bytes (the inverse operation)
  • ArrayBuffer — The raw binary container you pass to decode()
  • TypedArray — Typed array types used to represent the byte input

Written