window-size
window-size is a legacy CommonJS helper for reading terminal rows and columns in Node. At import time it tries `process.stdout`, then `process.stderr`, `COLUMNS` and `ROWS` environment variables, and an old `tty.getWindowSize` fallback. A successful result contains `width`, `height`, and an undocumented `type` describing the source; helper methods can query again or synchronously call `tput` and a Windows command. It also installs a small `window-size` executable.
Do not add `window-size` to a new Node CLI; the built-in TTY stream properties are enough for most programs, and `term-size` is a better fallback package. Keep it only for legacy compatibility, with explicit guards for an undefined export.
Use it if
- You maintain an existing CommonJS CLI whose behavior already depends on this package's fallback order
- You must support very old Node versions where `process.stdout.columns` and `rows` were inconsistent
- Your controlled environment supplies numeric `COLUMNS` and `ROWS` when stdout is not a TTY
- You need to preserve output compatibility with a transitive tool that expects `{ width, height, type }`
- You target current Node: `process.stdout.columns`, `process.stdout.rows`, `process.stdout.getWindowSize()`, and the `resize` event cover the normal TTY case without a package
- Your program runs in CI, a pipe, or redirected output without `COLUMNS` and `ROWS`: the main export can be `undefined`, which also means `.get()` and every helper disappear
- You want the bundled CLI to fail safely: `cli.js` reads `size.height` and `size.width` without checking the documented undefined result, so non-TTY execution can throw
- You plan to call `.win()`: the source runs WMIC against `Win32_VideoController` and returns display resolution, not the terminal window dimensions promised by the method name
- You need maintained TypeScript or ESM support: 1.1.1 was published in 2018, has no declarations or exports map, and carries two dependencies for logic current Node can express directly
Setup reality
`npm install window-size` adds a CommonJS module, a `window-size` executable, and two runtime dependencies. There are no native addons, peer dependencies, credentials, or config files. Import behavior is the main trap: the module measures once immediately and exports either the resulting plain object or `undefined`. It attaches `.get`, `.env`, `.tty`, `.tput`, and `.win` only when that first measurement succeeds. Therefore `require('window-size').get()` is unsafe in exactly the environments where a fallback helper might be needed. Requiring `window-size/utils` avoids that conditional method export and exposes the functions directly. The default query checks stdout before stderr, then accepts numeric `COLUMNS` and `ROWS`, then calls an old `tty.getWindowSize` API if present. The returned object also includes `type`, although the README's primary shape omits it. Resize handling is not automatic; listen on `process.stdout` and call `.get()` again, and remember the README warns that some platforms report only the initial dimensions. `.tput()` blocks the event loop with `execSync('tput cols && tput lines')`, depends on a Unix command and a meaningful terminal database, swallows all failures, and may write command errors to stderr. `.win()` also blocks, only runs when `os.release()` begins with `10`, invokes WMIC, and actually reads video-controller resolution rather than console geometry. The executable does not guard the undefined export, so it is unsuitable for pipes and CI unless size environment variables are guaranteed. No API observes `SIGWINCH` independently, distinguishes pixels from character cells, supplies defaults, or handles browser windows. For current Node, a small local helper that checks `stdout.isTTY`, `columns`, and `rows` is clearer and safer.
Patterns
Read the import-time terminal sizeread-initial-size
const size = require('window-size');
if (size) {
console.log(size.width, size.height, size.type);
} else {
console.log('Terminal size unavailable');
}The main export is `undefined` when no method succeeds. The `type` field identifies `stdout`, `stderr`, `process.env`, or `tty`.
Query dimensions againrefresh-current-size
const size = require('window-size');
const current = size?.get();
if (current) {
renderAtWidth(current.width);
}`.get` only exists when the initial import found a size, so optional access is required in pipes and non-TTY environments.
Update layout after a resizehandle-terminal-resize
const size = require('window-size');
if (size && process.stdout.isTTY) {
process.stdout.on('resize', () => {
const current = size.get();
if (current) redraw(current.width, current.height);
});
}The package does not subscribe for you, and its README warns that some platforms expose only the initial terminal size.
Read COLUMNS and ROWS directlyuse-environment-size
const utils = require('window-size/utils');
const environmentSize = utils.env();
if (environmentSize) {
console.log(environmentSize.width, environmentSize.height);
}Both values must pass the package's numeric check. This is useful in controlled CI only when you set both variables yourself.
Access helpers when the main export is undefinedaccess-utils-fallback
const size = require('window-size');
const utils = require('window-size/utils');
const current = size || utils.env() || utils.tput();Direct utils import is necessary because helper methods are attached only after the default import succeeds. `tput()` is synchronous and platform-dependent.
Test with a supplied stream shapequery-custom-stream
const utils = require('window-size/utils');
const size = utils.get({
stdout: { columns: 100, rows: 30 },
stderr: null,
});
console.log(size);The implementation checks real `process.stdout` before the supplied option, so this is not true dependency injection while normal process streams report dimensions.
Use the Unix tput fallback explicitlyquery-with-tput
const { tput } = require('window-size/utils');
const size = tput();
if (size) console.log(`${size.width}x${size.height}`);This blocks with `execSync`, requires `tput`, may send command errors to stderr, and silently returns undefined when the command fails.
Provide deterministic dimensions in CIsupply-ci-dimensions
// shell
// COLUMNS=120 ROWS=40 node report.js
const size = require('window-size');
const width = size?.width ?? 80;Environment values are considered only after stdout and stderr. Use an application default as the final fallback.
Read dimensions with current Node insteadavoid-package-modern-node
function terminalSize(stream = process.stdout) {
if (!stream.isTTY) return undefined;
return {
width: stream.columns,
height: stream.rows,
};
}For current Node this local helper is usually preferable: no import-time snapshot, dependencies, obsolete TTY fallback, or misleading Windows screen query.
Choose a bounded text widthwrap-text-safely
const size = require('window-size');
const detected = size?.width ?? 80;
const contentWidth = Math.max(20, Math.min(detected - 4, 120));
printWrapped(message, contentWidth);Always provide and bound a fallback because terminal columns may be missing, stale, or unreasonable in redirected and embedded environments.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| term-size | npm | You need a maintained terminal-size query with a current Node baseline and sensible fallbacks |
| terminal-size | npm | You want a promise-based cross-platform query that can invoke platform tools when stream properties are unavailable |
| cli-width | npm | You only need terminal columns for wrapping or layout and prefer a focused width API |