← textStream() for response/request/blob

v151 · fetch · member reference

Response.textStream()

Reads a Response body as a ReadableStream of UTF-8-decoded strings — the streaming counterpart of response.text(), defined on the Body mixin in the Fetch Standard.

Syntax

// WHATWG Fetch — Body mixin (normative IDL)
ReadableStream textStream();

// Call form
const stream = response.textStream();

Chromium's binding: [RuntimeEnabled=TextStreamMethod, CallWith=ScriptState, RaisesException] ReadableStream textStream(); in body.idl.

Source: Fetch Standard — textStream(); Chromium body.idl

Inputs

The method takes no parameters. Its only input is the receiver's own state: the Response object's body (null or a body with a ReadableStream) and whether that body is already disturbed or locked. There are no options, no encoding argument — decoding is always UTF-8.

Source: Fetch Standard — textStream() method steps

Outputs

Returns a ReadableStream whose chunks are strings — the body's bytes run through a TextDecoderStream set up with UTF-8, “regardless of the presence or the value of a Content-Type header and regardless of the presence or the value of a charset parameter”. If the body's byte length is not a multiple of the encoding's unit size, the decoder completes any trailing sequence at end-of-stream.

Null body: if the response's body is null, the method returns a new ReadableStream that is already closed (it yields no chunks and is not an error). Null-body calls are not single-use: every call creates a distinct closed stream object, and bodyUsed remains false throughout, because the unusable/disturbed contract only exists for non-null bodies.

Source: Fetch Standard — textStream() method steps; Encoding Standard — TextDecoderStream

Errors

TypeError — unusable receiver: if the response is unusable — its body is non-null and that body's stream is already disturbed or locked (for example after a prior text(), textStream(), or a locked body reader) — the method throws a TypeError synchronously. This is the only exception in the method steps, mirrored by the binding's RaisesException.

Malformed bytes are not exceptions: the decoder runs in the Encoding Standard's default (non-fatal) error mode — TextDecoderOptions.fatal defaults to false — so invalid UTF-8 sequences decode to U+FFFD replacement characters in the output chunks rather than throwing or erroring the stream.

Source: Fetch Standard — textStream() method steps + unusable definition; Encoding Standard — TextDecoderOptions / error mode

Context

Receiver: any Response instance — one resolved from fetch(), synthesized with new Response(...), or produced by a service worker / cache.

Exposure: Response is exposed in both Window and Worker contexts ([Exposed=(Window,Worker)] in Chromium's response.idl; the WPT suite runs with global=window,worker). No secure-context requirement and no permission prompt: the binding carries no [SecureContext] extended attribute and the method reads bytes the page already holds.

Availability: Chrome 151 per the milestone listing and BCD, gated at build time by the TextStreamMethod runtime feature (status stable at trunk).

Source: Chromium response.idl; WPT textstream.any.js; Chromium runtime_enabled_features.json5; chromestatus.com/feature/5146752165478400

Lifecycle

Immediate consumption — non-null bodies only: for a response with a body, calling textStream() starts the read at once — the WPT contract asserts bodyUsed becomes true synchronously, before any chunk is consumed (a non-null but empty body behaves the same way and then yields zero chunks).

Single use — non-null bodies only: while the returned stream is being read (and after it closes), the body's stream is disturbed, so the response is unusable — any further body method (text(), json(), a second textStream()) throws the TypeError above. Clone the response first (response.clone()) if two consumers are needed.

Null bodies are repeatable: with no body there is nothing to disturb — the WPT null-body test asserts bodyUsed stays false after repeated calls and that each call returns a different, independently closed stream object.

Stream lifetime: the returned stream is the body's stream piped through the decoder, so stream state (locked while a reader is attached, closed at end-of-body) follows ordinary pipe semantics.

Source: WPT textstream.any.js — bodyUsed and single-use contracts; Fetch Standard — textStream() method steps

Examples

// Stream a large text response as decoded chunks (never buffering it whole).
const response = await fetch("/large-log.txt");
if (!response.ok) throw new Error(`HTTP ${response.status}`);

const reader = response.textStream().getReader();
let total = 0;
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  total += value.length; // value is a string, UTF-8 decoded
}
console.log(`read ${total} characters`);
Source: WPT textstream.any.js — reader-loop shape; whatwg/fetch PR #1862

Compatibility

Response.textStream() — from BCD api/Response.json (checked 2026-07-26)
Engine / runtimeSupportNotes
Chrome151Matches the Chrome 151 milestone listing (Enabled by default); Chrome for Android and WebView (Android + iOS) are BCD mirrors
EdgemirrorBCD mirrors Chrome's data
FirefoxNot supportedBCD version_added: false (Firefox for Android mirrors); vendor signal recorded on the ChromeStatus entry: No signal
SafariNot supportedBCD version_added: false (Safari on iOS mirrors); the ChromeStatus entry records a Positive signal — a recorded signal, not an official WebKit standards position (a feature this small has none)
Node.js26.5.0BCD records version_added: 26.5.0
DenoUnknownNo BCD entry as of 2026-07-26

Support rows are from BCD only. Separately, vendor signals come from the ChromeStatus API feature record (checked 2026-07-26): Firefox “No signal”, Safari “Positive”, web developers “No signals”. No Baseline entry: the webstatus.dev query returns no feature (checked 2026-07-26).

Source: BCD api/Response.json; ChromeStatus API feature record; webstatus.dev query

Security and privacy

No new authority: the method needs no permission, no user gesture, and no secure context beyond what producing the Response already required — it only re-reads bytes the page already possesses (the same trust boundary as text() or arrayBuffer()).

Deterministic decoding: the decoder ignores Content-Type and charset and always uses UTF-8, so a server cannot change the decoding contract per response; malformed input becomes U+FFFD replacement text rather than an exception channel.

Disclosure/storage: nothing is persisted, transmitted, or shared across origins by this method itself; stream lifetime is bounded by the response's own lifetime.

Source: Fetch Standard — textStream() method steps; Encoding Standard — TextDecoderStream error mode