jsguides

TextEncoder

new TextEncoder()

TextEncoder is part of the Encoding API and converts JavaScript strings into UTF-8 encoded byte data. It lives on window in browsers and on the util module in Node.js. Every modern environment supports it.

Constructor

const encoder = new TextEncoder();

Creates a new TextEncoder instance. The constructor takes no arguments because the encoder has no configuration options---it always produces UTF-8 output and there is no alternative encoding to select. After creating an instance, you can check the encoding property to confirm what you are working with, then call encode to produce bytes from a string.

encoding

encoder.encoding  // "utf-8"

The encoding property is read-only and always returns the string UTF-8. TextEncoder only supports UTF-8, with no escape hatch for other character sets. If you need encodings like Latin-1 or Shift-JIS, you will need a third-party library. The built-in API is deliberately limited to this one encoding for simplicity and web compatibility, and the property exists so you can confirm what you are working with at any time. Once you have verified the encoding, the main method you will reach for is encode, which turns a string into bytes in a single pass.

encode()

Calling encode is the most common way to use TextEncoder. Pass a string and get back a Uint8Array containing the UTF-8 representation of each character. The method signature is straightforward.

encode(input)

Parameters

  • input (string) — The text to encode.

Returns Uint8Array — A new Uint8Array containing the UTF-8 encoded bytes. Every call allocates a fresh array, so for high-throughput encoding prefer encodeInto instead. The example below shows the byte layout for a mixed ASCII and CJK string, confirming that each character maps to the correct number of output bytes.

const encoder = new TextEncoder();
const bytes = encoder.encode("Hello, 世界");

console.log(bytes.length); // 13
console.log(bytes); // Uint8Array(13) [ 72, 101, 108, 108, 111, 44, 32, 228, 184, 150, 231, 149, 140 ]

Each character in the input string becomes one or more bytes. ASCII characters stay single-byte, while characters outside the ASCII range (like and ) use two to four bytes each. For workloads that encode many strings in a loop, allocating a new Uint8Array on every call adds GC pressure---each allocation must eventually be collected. The encodeInto() method avoids this by writing directly into a buffer you supply and reuse across iterations.

encodeInto()

encodeInto(input, destination)

Encodes a string directly into a pre-allocated Uint8Array. This avoids allocating a new array on every call, which matters in tight loops or high-frequency encoding. The destination buffer must be sized to hold the encoded output; if it is too small, encodeInto() stops early rather than throwing.

Parameters

  • input (string) — The text to encode.
  • destination (Uint8Array) — The buffer to write encoded bytes into. Must have enough space.

Returns { read: number, written: number } — An object telling you how many input characters were consumed and how many output bytes were written.

const encoder = new TextEncoder();
const buffer = new Uint8Array(10);
const result = encoder.encodeInto("Hello", buffer);

console.log(result.read);    // 5
console.log(result.written); // 5

A successful encodeInto() call returns matching read and written counts, but that only happens when the buffer is large enough. If the destination is too small, the method stops mid-stream and you must handle the leftover characters yourself. The next example demonstrates this exact failure mode and shows how to detect it.

The truncation gotcha

encodeInto() does not throw when the destination buffer is too small. It silently stops after filling the buffer. The return value tells you what happened:

const encoder = new TextEncoder();
const small = new Uint8Array(3);
const result = encoder.encodeInto("Hello", small);

console.log(result.read);    // 2  — only "He" fit in 3 bytes
console.log(result.written); // 3  — buffer is full

// "llo" was not encoded. You must handle the remainder yourself.
if (result.read < "Hello".length) {
    // remaining characters need a larger buffer
}

This catches developers who assume encodeInto() encodes everything. Always check result.read against the input length. Once you understand the truncation behavior, you can avoid it entirely by pre-allocating a buffer that is large enough for your data and reusing it across iterations to eliminate per-call allocations.

Pre-allocating for performance

// Slow: new Uint8Array every call
for (const line of lines) {
    const bytes = encoder.encode(line); // allocation each iteration
}

// Faster: reuse a buffer and grow only when needed
const buf = new Uint8Array(1024);
for (const line of lines) {
    const result = encoder.encodeInto(line, buf);
    processBytes(buf.slice(0, result.written));
    // If result.written === buf.length, buf was too small — grow it
}

Pre-allocating a reusable buffer and growing it only when needed keeps the GC quiet during bulk encoding. For workloads that process text as a stream rather than discrete chunks, the TextEncoderStream class wraps the same encoding logic in a transform stream that pipes data through incrementally.

TextEncoderStream

For streaming encoding with the Streams API, use TextEncoderStream:

const textEncoder = new TextEncoderStream();
const readable = textEncoder.readable;
const writable = textEncoder.writable;

const writer = writable.getWriter();
writer.write("some text");
writer.close();

// Or pipe a ReadableStream through the TextEncoderStream
const inputStream = new ReadableStream({ /* ... */ });
const outputStream = inputStream.pipeThrough(new TextEncoderStream());

TextEncoderStream has the same encoding, encode(), and encodeInto() as TextEncoder, but as a stream transform. It is useful when building text-processing pipelines with the Streams API.

Node.js notes

Node.js 11.0.0+ ships with util.TextEncoder globally. In older versions, you had to require it explicitly:

// Node 11+
const { TextEncoder } = require('util');
const encoder = new TextEncoder();

Node’s Buffer and Uint8Array share memory, so you can pass a Buffer as the destination in encodeInto() without copying. The examples below show practical patterns for encoding data---first a single-pass encode for binary storage, then an incremental approach for large inputs.

Examples

Encoding for binary storage

const encoder = new TextEncoder();
const data = JSON.stringify({ name: "张三", score: 99 });
const bytes = encoder.encode(data);

// bytes is a Uint8Array — ready to store in IndexedDB, send over WebSocket, etc.

The single-pass encode() call works well when you have the full string up front and a single destination. For large payloads that arrive in pieces or need to fit within a fixed buffer size, encodeInto() lets you process chunks incrementally without allocating a new array each time. The pattern below walks through a string in fixed-size slices and tracks progress with an offset.

Incremental encoding with encodeInto()

const encoder = new TextEncoder();
const chunkSize = 256;
let offset = 0;

function encodeChunk(text) {
    const remaining = text.length - offset;
    const buf = new Uint8Array(Math.min(chunkSize, remaining));
    const result = encoder.encodeInto(text.slice(offset), buf);

    offset += result.read;
    return { bytes: buf.slice(0, result.written), done: result.read === remaining };
}

See Also

  • ArrayBuffer — The buffer type that holds raw binary data
  • TypedArray — The typed array type returned by encode()
  • TextDecoder — Decodes UTF-8 bytes back into JavaScript strings