into-stream review
into-stream 9.1.0 wraps a string, promise, iterable, async iterable, byte view, ArrayBuffer, web `ReadableStream`, or object in a Node.js `Readable`. The new feature in 9.1 is direct web `ReadableStream` input. Binary mode converts typed-array views to `Uint8Array`; `intoStream.object()` keeps JavaScript values as object-mode chunks. Its small API is useful at boundaries where another Node library insists on a stream. It does not read files, buffer an existing stream, or make browser streams portable, and the package now requires Node 20 or newer.
Our install of into-stream 9.1.0 took 0.6 seconds, left a single 1 MB package with no audit findings, and passed Node 22 import checks while the browser build failed. Add it for Node 20+ adapter code that repeatedly turns values or iterables into `Readable` streams; use native constructors when one line already covers the input.
We installed it
| Install | ✓ · 0.6s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · ESM package with exports map |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does into-stream install cleanly?
Yes. In a fresh container with an empty cache, npm install into-stream finished in 0.6s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
Can into-stream run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does into-stream work with both ESM and CommonJS?
Yes. Both import 'into-stream' and require('into-stream') worked in Node 22 in our run. The package is published as ESM with an exports map.
Does into-stream include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
into-stream or readable-stream: which should you use?
readable-stream: Use readable-stream when a package needs the userland Node streams implementation and support across older runtimes. Our install of into-stream 9.1.0 took 0.6 seconds, left a single 1 MB package with no audit findings, and passed Node 22 import checks while the browser build failed.
When should you not use into-stream?
You need browser code: our esbuild browser bundle failed because the package imports node:stream, and the README describes a Node Readable result
Use it if
- A Node API expects a `Readable`, but your source is already a string, bytes, promise, iterable, or web `ReadableStream`
- You want an async iterable to respect Node stream backpressure without writing a custom `Readable` implementation
- You need object-mode chunks from objects or object iterables through the explicit `intoStream.object()` entry point
- Your supported runtime is Node 20 or newer and a default ESM import fits the codebase
- You need browser code: our esbuild browser bundle failed because the package imports `node:stream`, and the README describes a Node `Readable` result
- You pass streams through `child_process` stdio: the README says returned streams cannot be used in that option and recommends `fs.createReadStream()` for files
- Your production runtime is Node 18 or older: version 9 declares `node >=20`, while version 8 was the last line with a lower runtime floor
- You already have a file path or a Node readable stream: `fs.createReadStream()` or the existing stream avoids an extra wrapper
- You need a browser `ReadableStream` as output: version 9.1 accepts one as input but returns a Node.js `Readable`, so native web stream constructors or `Readable.toWeb()` fit that boundary better
Setup reality
We installed into-stream 9.1.0 in 0.6 seconds, and it left 1 package using 1 MB on disk. The published package was 24 KB unpacked, with 0 direct dependencies, 0 peer dependencies, bundled TypeScript declarations, and no npm audit findings. Both require() and ESM import worked in our Node 22 sandbox.
The package has no credentials, native build, or config file. It declares Node 20 as its minimum and imports node:stream; our browser build failed in esbuild for that reason. The output is always a Node Readable, including when version 9.1.0 receives a web ReadableStream. Browser-only code should stay on the web streams API.
Choose binary or object mode before wrapping the value. intoStream() accepts strings and byte-like chunks, while intoStream.object() is for arbitrary objects. Arrays are copied first, and each array element becomes one chunk. A promise is awaited inside the stream; if it rejects, consumers receive a stream error rather than a synchronous exception.
Backpressure is handled through Readable.from() and the internal async generator. Chunk boundaries still depend on the input: an iterable yields one chunk per element, while a plain string is yielded as one value. The README calls out one hard limit: these generated streams do not work as child_process stdio handles. Use an actual file stream when spawning a program that needs file input.
Patterns
Pipe a string into a Node destination stream-a-string
import intoStream from 'into-stream';
intoStream('hello
').pipe(process.stdout);Since version 8, a plain string is yielded as one value; do not depend on the wrapper splitting it into smaller chunks.
Wrap bytes as a readable stream stream-byte-array
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const readable = intoStream(bytes);Version 9 documents Uint8Array rather than Buffer as the binary input. Node Buffers are Uint8Array subclasses, but portable code can use the broader type.
Convert an ArrayBuffer to a Node stream stream-array-buffer
const response = await fetch(url);
const body = await response.arrayBuffer();
const readable = intoStream(body);The package converts ArrayBuffer and typed-array views to Uint8Array before yielding them. The complete buffer is already in memory.
Delay the input behind a promise stream-a-promise
const readable = intoStream(loadPayload());
readable.on('error', (error) => {
console.error('payload failed', error);
});
readable.pipe(destination);A rejected promise becomes an asynchronous stream error. Attach error handling before consumption starts.
Turn a generator into chunks stream-an-iterable
function* lines() {
yield 'first
';
yield 'second
';
}
const readable = intoStream(lines());Each iterable element is a chunk. Yield strings or byte views in binary mode, and do not assume adjacent chunks will stay separate downstream.
Wrap an async generator with backpressure stream-an-async-iterable
async function* rows() {
for (const id of ids) {
yield `${await loadRow(id)}
`;
}
}
const readable = intoStream(rows());The internal `Readable.from()` requests values as the consumer drains them, so the generator should keep cleanup logic in `finally` for early destruction.
Adapt a web ReadableStream in version 9.1 stream-web-readable
const response = await fetch(url);
if (!response.body) throw new Error('Missing response body');
const nodeReadable = intoStream(response.body);Version 9.1.0 added this input. The result is a Node `Readable`, and our browser bundling attempt failed because `node:stream` is required.
Emit records in object mode stream-objects
const records = [{id: 1}, {id: 2}];
const readable = intoStream.object(records);
readable.on('data', (record) => {
console.log(record.id);
});Use `.object()` for arbitrary values. Passing objects to the binary function can trigger Node chunk-type errors during consumption.
Finish a pipeline with promise error handling pipe-with-promises
import {pipeline} from 'node:stream/promises';
await pipeline(
intoStream(chunks),
transform,
destination,
);`pipeline()` propagates source, transform, and destination failures and tears down the chain; plain `.pipe()` does not give one completion promise.
Consume the generated readable asynchronously consume-with-for-await
for await (const chunk of intoStream(['a', 'b', 'c'])) {
console.log(chunk.toString());
}Node may expose string chunks as Buffer values depending on the consumer path. Decode at the boundary instead of assuming a JavaScript string.
Destroy a stream when a request aborts cancel-stream-work
const readable = intoStream(source);
signal.addEventListener('abort', () => {
readable.destroy(signal.reason);
}, {once: true});Destroying the readable stops future reads, but cancellation of the underlying async iterable depends on whether its iterator implements `return()` and cleanup.
Use a file stream for child process input avoid-child-stdio
import {createReadStream} from 'node:fs';
import {spawn} from 'node:child_process';
const child = spawn('gzip', ['-c'], {
stdio: [createReadStream('input.txt'), 'pipe', 'inherit'],
});The into-stream README says its returned streams cannot be passed as `child_process` stdio entries. A real file stream is the documented alternative.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| readable-stream | npm | Use readable-stream when a package needs the userland Node streams implementation and support across older runtimes. |
| from2 | npm | Use from2 when you need to implement a custom pull function instead of wrapping an existing iterable or value. |
| streamifier | npm | Use streamifier only for its older Buffer and string API when maintaining a compatible legacy codebase. |
| get-stream | npm | Use get-stream for the opposite direction: collecting a readable stream into text, bytes, or an array. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

