CONCEPT Cited by 1 source
BYOB reads (bring-your-own-buffer)¶
BYOB (bring-your-own-buffer) reads are the Web Streams mechanism for reducing per-chunk allocation in byte-oriented streams: instead of the stream allocating a new buffer for each read and handing it to the consumer, the consumer provides its own pre-allocated buffer and the stream fills it. The intent is zero-copy / buffer-reuse on high-throughput byte streams.
The intended semantics¶
const reader = stream.getReader({ mode: 'byob' });
const buffer = new ArrayBuffer(1024);
let view = new Uint8Array(buffer);
const result = await reader.read(view);
// 'view' should now be detached and unusable
// result.value is a NEW view, possibly over different memory
view = result.value; // must reassign
Key mechanics:
- A separate reader type:
ReadableStreamBYOBReader. - A separate controller:
ReadableByteStreamController. - A separate request type:
ReadableStreamBYOBRequest. ArrayBufferdetachment: the buffer passed in becomes unusable; the stream returns a new view over potentially different memory.
The complexity surface¶
The 2026-02-27 Cloudflare post enumerates the BYOB footprint:
- Separate API surface — a whole parallel reader type
means consumers need to know whether to call
getReader()orgetReader({ mode: 'byob' }). - Buffer lifecycle management — callers must track detachment and reassign.
- Detachment semantics — transferring an
ArrayBufferis error-prone and subtly different from normal JS value semantics. - Not composable with async
iteration —
for await…ofcan't access BYOB; consumers who want zero-copy are forced back into the manual reader loop. - Not composable with
TransformStream— same gap, from the other side. - Producer complexity — a correct byte-stream
implementation must handle both default and BYOB read
patterns simultaneously. The post shows the full producer
template:
controller.byobRequestbranch + defaultcontroller.enqueuebranch, both maintained in parallel. - WPT suite burden — BYOB has its own dedicated edge-case
test files: detached buffers, bad views, response-after-
enqueue ordering, WebAssembly memory rejection (BYOB reads
must explicitly reject
ArrayBuffers backed by Wasm memory, which look like regular buffers but can't be transferred).
The "complexity without payoff" critique¶
The post's argument is that BYOB is rarely used to any measurable benefit despite shipping all the above complexity:
"In practice, (and yes, there are always exceptions to be found) BYOB is rarely used to any measurable benefit. […] Most userland implementations of custom ReadableStream instances do not typically bother with all the ceremony required to correctly implement both default and BYOB read support in a single stream — and for good reason. It's difficult to get right and most of the time consuming code is typically going to fallback on the default read path."
The cost lands on both sides:
- Consumers rarely reach for BYOB because the ergonomics
(explicit reader type, detachment reassignment, no
for await…of) are worse than default reads. - Producers rarely implement BYOB correctly because getting both paths right is a double implementation burden.
- Implementers pay the full spec complexity — tracking pending BYOB requests, handling partial fills, coordinating between the BYOB reader and the underlying source.
- Runtimes can only realize the zero-copy benefit for host-provided streams (fetch response body) where the runtime controls both ends.
The implicit conclusion: a byte-oriented streaming API should either be default zero-copy (never require separate ceremony to skip allocation), or treat chunks opaquely and not expose partial-fill / buffer-reuse at all. systems/new-streams takes the latter position:
"While the API uses
Uint8Array, it treats chunks as opaque. There is no partial consumption, no BYOB patterns, no byte-level operations within the streaming machinery itself. Chunks go in, chunks come out, unchanged unless a transform explicitly modifies them."
Alternative views¶
Host-provided streams (runtime-produced bodies from fetch,
file reads, etc.) can benefit from BYOB when the runtime
implements it natively — the runtime knows both the consumer
buffer and the underlying byte source, can genuinely zero-copy.
Some production paths in Node.js / Deno / Bun / Workers do use
BYOB internally even when exposed through a default-read
surface. The critique is specifically about the user-facing
API mandating the BYOB surface.
Seen in¶
- sources/2026-02-27-cloudflare-a-better-streams-api-is-possible-for-javascript — canonical wiki instance: BYOB enumerated as one of the core Web streams design choices paying ongoing complexity for minimal real-world adoption.
Related¶
- systems/web-streams-api — the API where BYOB lives.
- concepts/async-iteration — the consumption model BYOB can't compose with.
- concepts/stream-adapter-overhead — related cost surface (Node ⇆ Web adapters) that also forces consumers between separate reader patterns.