# How to Take Input in Node.js: 4 Methods for Console User Interaction

> Discover 4 effective methods for how to take input in Node.js applications. Learn to interact with users via console using readline, promises, async iterators, and raw mode.

- Repository: [Node.js/node](https://github.com/nodejs/node)
- Tags: how-to-guide
- Published: 2026-02-20

---

**To take input in Node.js, use the built-in `node:readline` module for line-based interaction, `node:readline/promises` for async/await syntax, async iterators for continuous streams, or raw mode with `emitKeypressEvents` for character-level control.**

When building command-line applications with the `nodejs/node` runtime, capturing user input from the console requires understanding Node.js stream architecture. The `node:readline` module provides the canonical solution for how to take input in node js applications, wrapping `process.stdin` and `process.stdout` in a manageable interface that supports callbacks, promises, and async iteration.

## Understanding the Core Architecture

The Node.js input system operates through layered abstractions built on standard streams. At the foundation, `process.stdin` provides a **Readable** stream of raw bytes from the terminal, while `process.stdout` handles output as a **Writable** stream.

The [`lib/readline.js`](https://github.com/nodejs/node/blob/main/lib/readline.js) file implements the `Interface` class that buffers input until it detects end-of-line sequences (`\n`, `\r`, or `\r\n`). Upon detection, it emits a `'line'` event or resolves a `question` promise with the buffered string. This architecture ensures cross-platform compatibility across Windows, macOS, and Linux while maintaining non-blocking I/O.

## Method 1: Callback-Based Input with node:readline

The traditional approach uses the core `node:readline` module with callback functions. This pattern is implemented in [`lib/readline.js`](https://github.com/nodejs/node/blob/main/lib/readline.js) and remains the most widely supported method across all Node.js versions.

```javascript
const readline = require('node:readline');
const { stdin: input, stdout: output } = require('node:process');

const rl = readline.createInterface({ input, output });

rl.question('What is your name? ', (answer) => {
  console.log(`Hello, ${answer}!`);
  rl.close();
});

```

This method requires explicit resource management through `rl.close()` to prevent the process from hanging. The callback executes when the user presses Enter, receiving the input string minus the line terminator.

## Method 2: Promise-Based Input with node:readline/promises

Since Node.js v17.0.0, the `node:readline/promises` submodule provides a Promise-based API that enables cleaner async/await syntax. This implementation wraps the callback interface in [`lib/readline.js`](https://github.com/nodejs/node/blob/main/lib/readline.js) with Promise resolution logic.

```javascript
import * as readline from 'node:readline/promises';
import { stdin, stdout } from 'node:process';

const rl = readline.createInterface({ input: stdin, output: stdout });

try {
  const name = await rl.question('What is your name? ');
  console.log(`Hello, ${name}!`);
} finally {
  rl.close();
}

```

The Promise-based approach eliminates callback nesting and integrates naturally with modern JavaScript error handling through try/catch blocks. The `finally` clause ensures the interface closes even if exceptions occur.

## Method 3: Async Iterator for Continuous Input

For applications requiring continuous input processing, the `Interface` class implements `[Symbol.asyncIterator]`, allowing `for await...of` loops to consume lines lazily. This pattern is defined in [`lib/readline.js`](https://github.com/nodejs/node/blob/main/lib/readline.js) and is particularly efficient for processing large files or persistent user sessions.

```javascript
import { createInterface } from 'node:readline';
import { createReadStream } from 'node:fs';

const rl = createInterface({
  input: createReadStream('tasks.txt'),
  crlfDelay: Infinity,
});

for await (const line of rl) {
  console.log(`Task: ${line}`);
}
rl.close();

```

Setting `crllfDelay: Infinity` ensures consistent handling of Windows-style line endings (`\r\n`) across all platforms. This method maintains constant memory usage regardless of input size.

## Method 4: Raw Mode and Keypress Events

For character-level input control—such as password masking or real-time menu navigation—`readline.emitKeypressEvents` enables raw TTY mode. This functionality, implemented in [`lib/readline.js`](https://github.com/nodejs/node/blob/main/lib/readline.js), bypasses line buffering to emit `'keypress'` events for each keystroke.

```javascript
import { emitKeypressEvents } from 'node:readline';
import { stdin, stdout } from 'node:process';

emitKeypressEvents(stdin);
if (stdin.isTTY) stdin.setRawMode(true);

let password = '';
stdout.write('Enter password: ');

stdin.on('keypress', (char, key) => {
  if (key.name === 'return') {
    stdout.write('\n');
    console.log(`Your password is ${'*'.repeat(password.length)}`);
    stdin.setRawMode(false);
    stdin.pause();
  } else if (key.name === 'backspace') {
    password = password.slice(0, -1);
    stdout.clearLine(0);
    stdout.cursorTo(0);
    stdout.write('Enter password: ' + '*'.repeat(password.length));
  } else if (char) {
    password += char;
    stdout.write('*');
  }
});

```

Raw mode requires manual handling of special keys including Ctrl+C (SIGINT) and backspace. Always restore TTY state with `setRawMode(false)` before exiting to prevent terminal corruption.

## Key Source Files in the Node.js Repository

Understanding how to take input in node js applications is facilitated by examining the actual implementation in the `nodejs/node` repository:

- **[`lib/readline.js`](https://github.com/nodejs/node/blob/main/lib/readline.js)**: Contains the core `Interface` class, line buffering logic, and event emission systems.
- **[`doc/api/readline.md`](https://github.com/nodejs/node/blob/main/doc/api/readline.md)**: Official documentation covering all APIs, configuration options, and platform-specific behaviors.
- **[`test/parallel/test-readline-interface.js`](https://github.com/nodejs/node/blob/main/test/parallel/test-readline-interface.js)**: Comprehensive test suite validating edge cases including EOF handling, raw mode transitions, and Unicode input processing.

## Summary

- **Use `node:readline/promises`** for modern async/await syntax when processing single questions or sequential prompts.
- **Use `node:readline` with callbacks** for compatibility with Node.js versions prior to v17.0.0 or when working with legacy callback-based architectures.
- **Use async iteration** (`for await...of`) when processing continuous input streams, large files, or persistent CLI sessions to maintain constant memory usage.
- **Use raw mode with `emitKeypressEvents`** when requiring character-level control for password masking, real-time validation, or interactive menus.

## Frequently Asked Questions

### How do I handle password input without displaying characters on the screen?

Use `readline.emitKeypressEvents` to enable raw mode on `process.stdin`, then intercept each `'keypress'` event to mask output with asterisks while building the password string in memory. Remember to call `stdin.setRawMode(false)` before exiting to restore normal terminal behavior.

### What is the difference between `node:readline` and `node:readline/promises`?

The `node:readline` module provides a callback-based API where `rl.question()` accepts a callback function as its second argument. The `node:readline/promises` submodule, available since Node.js v17.0.0, exports an `Interface` class where `question()` returns a `Promise`, enabling cleaner `async/await` syntax and better error handling through try/catch blocks.

### Can I use the readline module to process files line by line?

Yes. Create a `readline.Interface` instance with a file stream as the `input` parameter (using `fs.createReadStream`), then consume lines using `for await (const line of rl)`. Set `crlfDelay: Infinity` in the options to handle Windows line endings correctly. This approach processes files lazily without loading the entire contents into memory.

### How do I prevent my Node.js script from hanging after collecting user input?

Always call `rl.close()` after finishing input collection to release the `Interface` instance and allow the event loop to exit. In promise-based code, place `rl.close()` in a `finally` block to ensure execution even if errors occur. For raw mode applications, additionally call `stdin.setRawMode(false)` and `stdin.pause()` to restore terminal state and stop listening for events.