Writes the current prompt to output and waits for user input. The optional
preserveCursor
parameter (boolean) determines if the cursor position should be preserved.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'CLI> '
// Display initial prompt
rl.prompt();
// Handle each line of input
rl.on('line', (line) => {
const input = line.trim();
switch (input) {
case 'hello':
console.log('Hello there!');
break;
case 'time':
console.log(`Current time: ${new Date().toLocaleTimeString()}`);
break;
case 'exit':
rl.close();
return;
default:
console.log(`You entered: ${input}`);
// Show the prompt again
rl.prompt();
// Handle application exit
rl.on('close', () => {
console.log('Goodbye!');
process.exit(0);
Tips for Using Prompts:
Use
rl.setPrompt()
to change the prompt string dynamically
For multi-line prompts, include line breaks in the prompt string
Consider using a library like
chalk
to add colors to your prompts
Remember to call
rl.prompt()
after handling each input to show the prompt again
3.
rl.write(data[, key])
Writes data to the output stream. The optional
key
parameter can be used to simulate key presses.
Write Example
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
// Display a welcome message
rl.write('Welcome to the CLI Application!\n');
rl.write('='.repeat(30) + '\n\n');
// Pre-fill a default value
rl.question('Enter your username: ', (username) => {
console.log(`Hello, ${username}!`);
// Simulate typing a default value
rl.write('
[email protected]');
// Move cursor to the beginning of the line
rl.write(null, { ctrl: true, name: 'a' });
rl.question('Enter your email: ', (email) => {
console.log(`Your email is: ${email}`);
rl.close();
Common Use Cases for
rl.write()
:
Displaying headers or section titles
Providing default values in prompts
Simulating user input for testing
Creating custom input formatting
4.
rl.close()
Closes the readline interface and releases control of the input and output streams. This is important to free up system resources and allow your program to exit cleanly.
Proper Cleanup Example
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
// Handle application exit
function exitApp() {
console.log('\nCleaning up resources...');
// Perform any necessary cleanup
// (e.g., close database connections, write logs, etc.)
// Close the readline interface
rl.close();
// Handle Ctrl+C
rl.on('SIGINT', () => {
console.log('\nReceived SIGINT. Exiting...');
exitApp();
// Handle normal exit
rl.on('close', () => {
console.log('Goodbye!');
process.exit(0);
// Start the application
console.log('Application started. Press Ctrl+C to exit.');
rl.prompt();
5.
rl.pause()
and
rl.resume()
These methods allow you to temporarily pause and resume the input stream, which can be useful for controlling the flow of user input.
Pause/Resume Example
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
let isPaused = false;
console.log('Type "pause" to pause input, "resume" to continue, or "exit" to quit');
rl.on('line', (input) => {
const command = input.trim().toLowerCase();
switch (command) {
case 'pause':
if (!isPaused) {
console.log('Input paused. Type "resume" to continue...');
rl.pause();
isPaused = true;
break;
case 'resume':
if (isPaused) {
console.log('Resuming input...');
rl.resume();
rl.prompt();
isPaused = false;
break;
case 'exit':
console.log('Goodbye!');
rl.close();
return;
default:
if (!isPaused) {
console.log(`You entered: ${input}`);
rl.prompt();
rl.prompt();
When to Use Pause/Resume:
When performing long-running operations that shouldn't be interrupted by user input
When temporarily disabling user input during certain operations
When implementing a paging mechanism for long outputs
When you need to throttle input processing
rl.question(query, callback)
Displays the query and waits for user input, then invokes the callback with the user's answer
rl.prompt([preserveCursor])
Displays the configured prompt for user input
rl.write(data[, key])
Writes data to the output stream, can also simulate keypress events
rl.close()
Closes the readline interface and relinquishes control of the input and output streams
rl.pause()
Pauses the readline input stream
rl.resume()
Resumes the readline input stream
// Promise-based question function
function askQuestion(query) {
return new Promise(resolve => {
rl.question(query, resolve);
// Using async/await with readline
async function main() {
try {
const name = await askQuestion('What is your name? ');
console.log(`Hello, ${name}!`);
const age = await askQuestion('How old are you? ');
console.log(`In 5 years, you'll be ${parseInt(age) + 5} years old.`);
const location = await askQuestion('Where do you live? ');
console.log(`${location} is a great place!`);
console.log('Thank you for your answers!');
} catch (error) {
console.error('Error:', error);
} finally {
rl.close();
// Run the main function
main();
// Function to display menu
function displayMenu() {
console.log('\n===== MAIN MENU =====');
for (const [key, value] of Object.entries(menu)) {
console.log(`${key}: ${value}`);
console.log('====================\n');
// Function to handle menu selection
async function handleMenu() {
let running = true;
while (running) {
displayMenu();
const answer = await askQuestion('Select an option: ');
switch (answer) {
case '1':
console.log(`Current time: ${new Date().toLocaleTimeString()}`);
break;
case '2':
console.log('System info:');
console.log(`Platform: ${process.platform}`);
console.log(`Node.js version: ${process.version}`);
console.log(`Process ID: ${process.pid}`);
break;
case '3':
const memory = process.memoryUsage();
console.log('Memory usage:');
for (const [key, value] of Object.entries(memory)) {
console.log(`${key}: ${Math.round(value / 1024 / 1024 * 100) / 100} MB`);
break;
case '4':
console.log('Exiting program. Goodbye!');
running = false;
break;
default:
console.log('Invalid option. Please try again.');
if (running) {
await askQuestion('\nPress Enter to continue...');
console.clear(); // Clear console for better UX
// Promise-based question function
function askQuestion(query) {
return new Promise(resolve => {
rl.question(query, resolve);
// Start the interactive menu
handleMenu()
.finally(() => {
rl.close();
// Create a readable stream for the file
const fileStream = fs.createReadStream('example.txt');
// Create the readline interface
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity // Recognize all instances of CR LF as a single line break
// Counter for line numbers
let lineNumber = 0;
// Process file line by line
rl.on('line', (line) => {
lineNumber++;
console.log(`Line ${lineNumber}: ${line}`);
// Handle end of file
rl.on('close', () => {
console.log(`Finished reading file with ${lineNumber} lines.`);
Note:
This approach is memory-efficient for large files as it processes one line at a time rather than loading the entire file into memory.
Tab Completion
Tab completion enhances the user experience by providing command and file path suggestions. The Readline module allows you to implement custom completion logic:
How Tab Completion Works:
User presses the Tab key
Readline calls your completer function with the current line and cursor position
Your function returns completion suggestions
Readline displays the completions or auto-completes if there's only one match
const readline = require('readline');
const fs = require('fs');
const path = require('path');
// Available commands for autocompletion
const commands = ['help', 'exit', 'list', 'clear', 'info', 'version', 'history'];
// Create the readline interface with a completer function
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'myapp> ',
completer: function(line) {
// Command completion
if (!line.includes(' ')) {
const hits = commands.filter(c => c.startsWith(line));
// Show all completions if none matches
return [hits.length ? hits : commands, line];
// File path completion (for commands like "list ")
if (line.startsWith('list ')) {
const dir = line.split(' ')[1] || '.';
try {
let completions = fs.readdirSync(dir);
// Add trailing slash to directories
completions = completions.map(file => {
const fullPath = path.join(dir, file);
return fs.statSync(fullPath).isDirectory() ? file + '/' : file;
const hits = completions.filter(c => c.startsWith(line.split(' ')[1] || ''));
return [hits.length ? hits : completions, line.split(' ')[1] || ''];
} catch (err) {
return [[], line];
return [[], line];
// Set the prompt
rl.prompt();
// Handle commands
rl.on('line', (line) => {
line = line.trim();
switch (true) {
case line === 'help':
console.log('Available commands:');
commands.forEach(cmd => console.log(` ${cmd}`));
break;
case line === 'exit':
console.log('Goodbye!');
rl.close();
return;
case line.startsWith('list'):
const dir = line.split(' ')[1] || '.';
try {
const files = fs.readdirSync(dir);
console.log(`Contents of ${dir}:`);
files.forEach(file => {
const stats = fs.statSync(path.join(dir, file));
console.log(` ${file}${stats.isDirectory() ? '/' : ''}`);
} catch (err) {
console.error(`Error listing directory: ${err.message}`);
break;
case line === 'clear':
console.clear();
break;
case line === 'info':
console.log('Node.js CLI Example');
console.log(`Version: 1.0.0`);
break;
case line === 'version':
console.log(`Node.js version: ${process.version}`);
break;
case line === 'history':
// Note: This requires storing history manually
console.log('History feature not fully implemented.');
break;
case line === '':
// Do nothing for empty lines
break;
default:
console.log(`Unknown command: ${line}`);
console.log('Type "help" for available commands');
rl.prompt();
}).on('close', () => {
console.log('CLI terminated.');
process.exit(0);
// Handle Ctrl+C
rl.on('SIGINT', () => {
rl.question('Are you sure you want to exit? (y/n) ', (answer) => {
if (answer.toLowerCase() === 'y') {
rl.close();
} else {
rl.prompt();
Multi-Line Input
The Readline module excels at handling multi-line input, making it perfect for text editors, code editors, or any application that needs to collect multiple lines of text from users. Here's how to implement it effectively:
Multi-Line Input Strategies:
End Marker:
Use a special sequence (like
.end
) to signal the end of input
Bracket Matching:
Automatically detect when all opened brackets/parentheses are closed
Dedicated Command:
Provide a specific command to submit multi-line input
Timeout-Based:
Use a timer to detect when the user is done typing
console.log('Enter your multi-line input. Type ".end" on a new line to finish:');
rl.prompt();
// Store lines
const lines = [];
// Handle input
rl.on('line', (line) => {
// Check for end command
if (line.trim() === '.end') {
console.log('\nYour complete input:');
console.log('-'.repeat(30));
console.log(lines.join('\n'));
console.log('-'.repeat(30));
// Process the input (example: count words)
const text = lines.join(' ');
const wordCount = text.split(/\s+/).filter(Boolean).length;
const charCount = text.length;
console.log(`\nStatistics:`);
console.log(`Lines: ${lines.length}`);
console.log(`Words: ${wordCount}`);
console.log(`Characters: ${charCount}`);
rl.close();
return;
// Add line to collection
lines.push(line);
rl.prompt();
Building a Simple REPL
A Read-Eval-Print Loop (REPL) is an interactive programming environment that reads user input, evaluates it, and prints the result.
The Readline module is perfect for building custom REPLs. Here's a comprehensive guide to creating your own:
Key Components of a REPL:
Read:
Accept user input line by line
Eval:
Parse and evaluate the input
Print:
Display the result or any output
Loop:
Return to the input prompt for the next command
Special Commands:
Handle commands like
.help
,
.exit
Error Handling:
Gracefully handle syntax errors and exceptions
Object,
// Add any other global variables you want to make available
// You can also add your own functions
add: (a, b) => a + b,
multiply: (a, b) => a * b
console.log('Simple JavaScript REPL (Press Ctrl+C to exit)');
console.log('Type JavaScript code and press Enter to evaluate');
// Show the prompt
rl.prompt();
// Track multi-line input
let buffer = '';
// Handle input
rl.on('line', (line) => {
// Add the line to our buffer
buffer += line;
try {
// Try to evaluate the code
const result = vm.runInContext(buffer, context);
// Display the result
console.log('\x1b[33m%s\x1b[0m', '=> ' + result);
// Reset the buffer after successful evaluation
buffer = '';
} catch (error) {
// If it's a syntax error and might be due to incomplete input,
// continue collecting input
if (error instanceof SyntaxError &&
error.message.includes('Unexpected end of input')) {
// Continue in multi-line mode
rl.setPrompt('... ');
} else {
// It's a real error, show it and reset buffer
console.error('\x1b[31m%s\x1b[0m', 'Error: ' + error.message);
buffer = '';
rl.setPrompt('js> ');
rl.prompt();
// Handle Ctrl+C
rl.on('SIGINT', () => {
if (buffer.length > 0) {
// If there's pending input, clear it
console.log('\nInput cleared');
buffer = '';
rl.setPrompt('js> ');
rl.prompt();
} else {
// Otherwise exit
rl.question('\nAre you sure you want to exit? (y/n) ', (answer) => {
if (answer.toLowerCase() === 'y') {
console.log('Goodbye!');
rl.close();
} else {
rl.prompt();
rl.on('close', () => {
console.log('REPL closed');
process.exit(0);
Always close the interface
: Call
rl.close()
when you're done to clean up resources.
Handle errors
: Implement error handling for all readline operations.
Use promises
: Wrap callback-based methods in promises for cleaner async code.
Consider UX
: Provide clear prompts and feedback to users.
Use event handlers
: Leverage the event-based nature of readline for complex interactions.
Memory management
: Be careful with large files; process them line by line.
Add keyboard shortcuts
: Implement handlers for common keyboard shortcuts (Ctrl+C, Ctrl+D).
Readline
Line-by-line processing, interactive input, history, completion
More code for complex interfaces
CLIs, interactive prompts, REPLs
process.stdin
Low-level control, raw data
Harder to use, manual buffering
Binary input, custom protocols
Inquirer.js (3rd party)
Rich UI, many input types, validation
External dependency
Complex forms, surveys, config wizards
Commander.js (3rd party)
Command definition, option parsing
Less interactive
Command-line tools with arguments
Summary
The Node.js Readline module provides a simple yet powerful way to create interactive command-line interfaces, process text input line by line, and build tools that require user interaction.
It's particularly useful for:
Creating interactive command prompts
Building CLI applications with user input
Processing files line by line
Implementing custom REPL environments
Developing text-based interfaces and games
While simple on the surface, combining Readline with promises, event handling, and proper UX considerations allows you to build sophisticated command-line applications that provide a great user experience.
Contact Sales
If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
[email protected]
Report Error
If you want to report an error, or if you want to make a suggestion, send us an e-mail:
[email protected]
W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning.
Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness
of all content. While using W3Schools, you agree to have read and accepted our
terms of use
,
cookie and privacy policy
.
W3Schools is Powered by W3.CSS
.