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

write

write is a small CommonJS helper for putting strings, buffers, or Uint8Arrays into files from Node.js. Its useful trick is creating missing parent directories before it writes, whether you use its promise, callback, synchronous, or writable-stream entry point. It can also add a final newline, refuse to replace an existing file, or choose an incremented filename such as report (2).txt. Think of it as a convenience layer over Node's fs module, not a storage system or a durability tool.

Verdict

It still does its tiny job, especially in old CommonJS generators that use every API form. For new code, the built-in fs promises are usually clearer, typed, and one fewer unmaintained dependency; use write-file-atomic when durability is the actual requirement.

API stability4/5The version 2 surface is only a callable function plus sync and stream methods, with three documented convenience options, so there is little API to churn. The npm release and repository source have remained unchanged since September 2019. That long freeze makes existing behavior predictable, but it is stagnation rather than an actively managed compatibility promise, and the README's contents wording already disagrees with the returned data property in source.
Docs3/5The README documents every public method, all three custom options, accepted data types, Node 10 requirement, and examples for promises, callbacks, sync writes, and streams. It is enough to start without reading code. It does not discuss concurrency, atomicity, TypeScript, ESM, error timing, or stream completion, and its return-value prose says contents while the implementation returns data, a concrete accuracy gap users can hit.
Maintenance1/5npm shows version 2.0.0 published on September 4, 2019, and GitHub reports the last repository push on the same date. The repository is not archived and has only four open issues and PRs, but there has been no visible maintenance for nearly seven years. A stable file helper may need few changes, yet the missing ESM and TypeScript support and old Node baseline show that the package has not followed its ecosystem.
Ecosystem3/5The package recorded 3,790,397 downloads from July 31 through August 6, 2026, so it remains deeply present in dependency trees despite only 82 GitHub stars. It uses normal Node streams and fs option objects, which makes it easy to combine with existing code, but its extension ecosystem is effectively just one dependency for filename increments and its functionality now overlaps heavily with the standard library.

Use it if

  • You maintain CommonJS code that repeatedly needs to create parent directories and then write a file
  • You need the same small helper to expose promise, callback, synchronous, and writable-stream forms
  • You generate reports, logs, or exports and want automatic numbered filenames instead of replacing an existing file
  • You want an optional trailing newline without repeating end-of-file checks around every write
Skip it if

Setup reality

Installation is only npm install write, with no native build, credentials, configuration file, or peer dependency. Version 2.0.0 requires Node 10 or newer and is a CommonJS package, so require('write') is the documented path; an ESM project has to rely on Node's CommonJS interop. There are no bundled TypeScript declarations, which means strict TypeScript projects need a local declare module shim or their own small wrapper. Every API creates parent directories automatically and replaces the destination by default. Set overwrite: false when replacement must be rejected, but do not mistake that check for concurrency control because the source checks existence before opening the file. The increment option also chooses a name through a separate filesystem check, so simultaneous writers may still select the same destination. The asynchronous function resolves to an object containing path and data; the README prose calls the second property contents, but the version 2.0.0 source actually uses data. The stream method creates directories synchronously before returning a normal fs.WriteStream, so listen for error and wait for finish or close before treating output as complete. The newline option only adds a line feed when the data is a string or Buffer and lacks one already. Finally, maintenance is the main adoption cost: the npm release and last repository push both date to September 2019, so test it against your supported Node versions and do not expect modern module or typing improvements.

Patterns

Write a file and create its parent directorieswrite-file

const write = require('write');

const result = await write('dist/reports/today.txt', 'ready');
console.log(result.path, result.data);

Missing dist and reports directories are created recursively. The returned object uses data, although the README description calls that property contents.

Use the callback formwrite-with-callback

const write = require('write');

write('dist/status.txt', 'ok', (error, result) => {
  if (error) {
    console.error(error);
    return;
  }
  console.log('wrote', result.path);
});

Providing a callback makes the function return undefined instead of a promise. Handle the callback error before reading the result.

Write binary data from a Bufferwrite-buffer

const write = require('write');

const bytes = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
await write('dist/image.bin', bytes);

Strings, Buffers, and Uint8Arrays are accepted. Do not enable newline for binary data unless an added line-feed byte is intentional.

Ensure a text file ends with a newlineensure-newline

const write = require('write');

await write('dist/generated.conf', 'enabled=true', {
  newline: true,
});

newline: true adds one line feed only when the string or Buffer does not already end in one. It does not normalize CRLF line endings.

Reject an existing destinationprevent-overwrite

const write = require('write');

try {
  await write('exports/report.csv', csv, { overwrite: false });
} catch (error) {
  if (!String(error.message).startsWith('File already exists:')) throw error;
}

The existence check is not atomic. Use exclusive fs flags or an atomic-write package when multiple processes can target the same path.

Keep both files by incrementing the new nameincrement-filename

const write = require('write');

const result = await write('exports/report.csv', csv, {
  increment: true,
});
console.log(result.path); // exports/report (2).csv when report.csv exists

Always use result.path after an incremented write because it may differ from the requested path. Name selection is not safe against simultaneous writers.

Write during a synchronous build stepwrite-synchronously

const write = require('write');

const result = write.sync('dist/meta/version.txt', '2.4.0', {
  newline: true,
});
console.log(result.path);

sync blocks the event loop and creates directories synchronously. Keep it to startup scripts and short build tasks, not request handlers.

Pipe a readable stream into a nested filepipe-to-file

const fs = require('node:fs');
const write = require('write');

const output = write.stream('backup/docs/README.md');
output.on('error', console.error);
output.on('close', () => console.log('closed'));
fs.createReadStream('README.md').pipe(output);

write.stream returns a standard fs.WriteStream and creates its parent directory before returning. Listen for errors on both input and output in production.

Pass fs options through to the writerset-file-mode

const write = require('write');

await write('dist/run.sh', '#!/bin/sh\necho ok\n', {
  mode: 0o755,
});

Options are forwarded to fs.createWriteStream. On an existing file, mode does not necessarily replace its current permissions; use chmod when that distinction matters.

Write text with an explicit encodingchoose-encoding

const write = require('write');

await write('dist/latin1.txt', 'café', {
  encoding: 'latin1',
});

The package defaults encoding to utf8. Explicit non-UTF-8 encodings only apply when the input is a string.

Wait for a streaming write to finishawait-stream-finish

const { finished } = require('node:stream/promises');
const write = require('write');

const output = write.stream('dist/events.log');
output.end('started\n');
await finished(output);

Creating or ending the stream does not mean bytes are flushed. Await finished before moving, reading, or announcing the file.

Replace an existing file explicitlyreplace-existing-file

const write = require('write');

await write('dist/latest.json', JSON.stringify(payload, null, 2), {
  overwrite: true,
  newline: true,
});

Replacement is already the default. This direct write is not atomic, so a crash can leave a partial file visible to readers.

Alternatives

PackageRegistryPick it when
fs-extranpmChoose it when you also need copy, move, remove, JSON, and outputFile helpers in one maintained filesystem toolkit
write-file-atomicnpmChoose it when readers must never observe a partially written destination and atomic replacement matters
output-file-syncnpmChoose it for a narrowly focused synchronous helper that creates ancestor directories before writing