mrkeyoor.com_
Sat 08 Aug 21:00 UTC
npmUtilsupdated 08 Aug 2026

to-readable-stream

to-readable-stream wraps one already available JavaScript value in a WHATWG ReadableStream. The stream enqueues that exact value as its only chunk and then closes, with generic TypeScript inference preserving the chunk type. Strings stay strings, Uint8Arrays stay bytes, and objects stay objects. Despite the name, it does not create a Node stream.Readable, split large values, encode text, iterate collections, or lazily pull from a source. It is a four-line convenience for APIs that specifically accept web streams.

Verdict

Use it only when one in-memory value must satisfy a web ReadableStream parameter and preserving that exact chunk type is desirable. For almost everything else, a direct ReadableStream constructor or the native Node stream conversion APIs are clearer and avoid a dependency.

API stability4/5Version 4 has one default function, no options, no dependencies, and one generic declaration, so there is almost nothing for a minor release to destabilize. The exact one-chunk behavior is visible in four lines of source. Major versions have still moved environmental requirements: the current line is ESM-only and requires Node 18, which matters to CommonJS and older-runtime consumers even though the function's conceptual API is unchanged.
Docs3/5The README is concise, shows the single call, links WHATWG ReadableStream, warns that the result is not Node stream.Readable, recommends Readable.from for that case, and points to a richer related package. It does not explain that the value is emitted as exactly one unchanged chunk, that strings are not encoded, how byte-oriented Response consumers behave, when memory use is unchanged, or how to consume, tee, pipe, and bridge the returned stream.
Maintenance3/5Version 4.0.0 and the last repository push both date to October 26, 2022. The repository is not archived and GitHub reports zero open issues and pull requests. A four-line adapter over a standard global can be finished rather than abandoned, so inactivity is less concerning than it would be for a parser or framework. Still, there has been no published verification against several years of Node and web-stream interoperability changes.
Ecosystem3/5The package recorded 4,724,626 downloads for the fetched week, has no runtime dependencies, includes TypeScript inference, and uses the standard WHATWG ReadableStream type available in browsers and modern Node. Direct community interest is modest at 93 GitHub stars, and native ReadableStream plus Node's Readable.from, fromWeb, and toWeb methods cover most neighboring cases without another package. Its usage is broad but the unique ecosystem value is narrow.

Use it if

  • You have one value in memory and an API specifically requires a WHATWG ReadableStream
  • You want the emitted chunk to retain its exact JavaScript and TypeScript type
  • You are on Node 18 or newer or a browser with the global ReadableStream constructor, and your project uses ESM
Skip it if

Setup reality

Install the package and default-import it from an ES module. Version 4 is ESM-only, has no runtime dependencies, includes a generic TypeScript declaration, and declares Node 18 or newer. It relies on the global WHATWG ReadableStream constructor; it does not import a ponyfill. The critical setup decision is chunk type. The function does not convert anything. toReadableStream('hello') produces one string chunk, not UTF-8 bytes. That is fine for your own reader or a TransformStream that expects strings, but byte-oriented consumers such as Node's Fetch implementation can reject a non-Uint8Array chunk. Encode text first with TextEncoder when feeding Response, compression, hashing, file, or protocol APIs that expect bytes. Buffer works in Node because Buffer extends Uint8Array, but Buffer is not a browser global. Objects are accepted and typed, yet only custom object-mode web-stream consumers know what to do with them. The input is already fully materialized before the stream exists, and start enqueues it immediately, so wrapping a large Buffer does not lower peak memory or add useful backpressure. Cancellation cannot stop upstream work because there is no upstream work. Each call makes a fresh one-shot stream; once a reader locks or consumes it, create another stream or call tee before reading when two consumers need the same chunk. If you need a Node Readable, use Readable.from as the README recommends. If you need several chunks, an iterable, an async producer, or a pull/cancel implementation, construct ReadableStream directly or choose the related into-stream package for a richer Node-stream conversion. For Node-to-web interoperability, Readable.toWeb and Readable.fromWeb are clearer because they state which stream model crosses the boundary.

Patterns

Wrap a string as one chunkstream-string-chunk

import toReadableStream from 'to-readable-stream'

const stream = toReadableStream('hello')
const {value, done} = await stream.getReader().read()
console.log(value, done)

value is the string hello and done is false on the first read. A second read returns done true. No text encoding occurs.

Encode text before creating a byte streamstream-utf8-bytes

import toReadableStream from 'to-readable-stream'

const bytes = new TextEncoder().encode('hello')
const stream = toReadableStream(bytes)

Encode explicitly for consumers that require Uint8Array chunks. The package never converts string chunks to bytes.

Wrap a Node Bufferstream-node-buffer

import toReadableStream from 'to-readable-stream'

const stream = toReadableStream(Buffer.from('hello'))

Buffer is a Uint8Array subclass in Node, so it works for byte consumers. Use TextEncoder for browser-portable code.

Emit a typed objectstream-object-chunk

import toReadableStream from 'to-readable-stream'

const stream = toReadableStream({id: 1, name: 'Ada'})
const reader = stream.getReader()
const {value} = await reader.read()
console.log(value.name)

The object is emitted by reference and is not cloned or serialized. Only custom consumers should expect object chunks.

Read the one-shot stream manuallyread-to-completion

const reader = toReadableStream(value).getReader()
try {
  while (true) {
    const result = await reader.read()
    if (result.done) break
    consume(result.value)
  }
} finally {
  reader.releaseLock()
}

There is exactly one value, but the normal reader loop keeps code compatible with other ReadableStream sources.

Consume with async iterationiterate-stream

for await (const chunk of toReadableStream(value)) {
  console.log(chunk)
}

Modern Node web streams support async iteration. Some older browser type libraries may not expose Symbol.asyncIterator even when getReader works.

Decode an emitted byte chunkdecode-byte-stream

const bytes = new TextEncoder().encode('hello')
const textStream = toReadableStream(bytes).pipeThrough(new TextDecoderStream())

for await (const text of textStream) {
  console.log(text)
}

TextDecoderStream expects byte chunks. Passing toReadableStream('hello') would provide the wrong chunk type.

Use bytes as a Response bodycreate-fetch-response

const body = toReadableStream(new TextEncoder().encode('hello'))
const response = new Response(body, {
  headers: {'content-type': 'text/plain; charset=utf-8'}
})

Use Uint8Array chunks for Fetch-compatible bodies. A raw string chunk can be rejected by byte-oriented Response consumers.

Split before consumingtee-two-consumers

const source = toReadableStream(bytes)
const [forHashing, forUpload] = source.tee()

ReadableStream is one-shot once consumed. Call tee before acquiring a reader when two consumers need the value.

Pipe the value to a WritableStreampipe-to-sink

const received = []
const sink = new WritableStream({
  write(chunk) {
    received.push(chunk)
  }
})

await toReadableStream(value).pipeTo(sink)

pipeTo receives the original value as one write call. The wrapper does not divide it according to sink backpressure.

Convert byte chunks to a Node Readablebridge-to-node-stream

import {Readable} from 'node:stream'
import toReadableStream from 'to-readable-stream'

const webStream = toReadableStream(new TextEncoder().encode('hello'))
const nodeStream = Readable.fromWeb(webStream)

If the final consumer wants Node streams, Readable.from([value]) is usually simpler, as the package README recommends.

Preserve the chunk type in TypeScriptpreserve-typescript-type

import toReadableStream from 'to-readable-stream'

type Event = {type: 'created'; id: string}
const event: Event = {type: 'created', id: '42'}
const stream: ReadableStream<Event> = toReadableStream(event)

The generic declaration infers the exact input type. It does not constrain values to strings, Buffer, or Uint8Array.

Alternatives

PackageRegistryPick it when
into-streamnpmYou need a richer Node stream converter for strings, promises, arrays, iterables, async iterables, buffers, typed arrays, or objects
web-streams-polyfillnpmYour runtime lacks WHATWG streams and you need the constructors before building a stream directly
readable-streamnpmYour consumer expects the Node streams API and you need the userland implementation across Node versions
streamxnpmYou want an alternative Node-style stream implementation with a broader producer and transform model