Drop-dead simple keypress event emitter for Node.js. Create powerful CLI applications and experiences with ease.
Please consider following this project's author, Jon Schlinkert, and consider starring the project to show your ❤️ and support.
Install with npm:
$ npm install --save emit-keypressNode's built-in readline module is great for handling user input if you need something simple. But when using createInterface, there are a number of built-in behaviors that are difficult or impossible to override when you need more control over the input stream.
Other keypress modules I found either did too much, or didn't allow for enough customization. This module is designed to be simple, flexible, easy to use, and easy to customize.
Create your own CLI interface
It's lightweight with only one dependency for detecting the terminal. It's easy to use, and easy to customize. It's designed to be used in a wide range of use-cases, from simple command-line utilities to complex terminal applications.
Powerful CLI applications can be built with this module.
import { emitKeypress } from 'emit-keypress';
emitKeypress({ input: process.stdin });
process.stdin.on('keypress', (input, key) => {
console.log({ input, key });
if (input === '\x03' || input === '\r') {
process.stdin.pause();
}
});Pass an onKeypress function to emitKeypress to handle keypress events.
// The "close" function is passed as the third argument
const onKeypress = async (input, key, close) => {
// do stuff with keypress events
console.log({ input, key });
// Close the stream if the user presses `Ctrl+C` or `Enter`
if (input === '\x03' || input === '\r') {
close();
}
};
emitKeypress({ onKeypress });A close function is also returned from emitKeypress that can be called to close the stream.
const { close } = emitKeypress({ onKeypress });
// close the stream
setTimeout(() => {
close();
}, 10_000);Pass a keymap array to map keypress events to specific shortcuts.
emitKeypress({
keymap: [
{ sequence: '\x03', shortcut: 'ctrl+c' },
{ sequence: '\r', shortcut: 'return' }
],
onKeypress: async (input, key, close) => {
// do stuff with keypress events
console.log({ input, key });
if (key.shortcut === 'return' || key.shortcut === 'ctrl+c') {
close();
}
}
});Note that you can add arbitrary properties the keymap objects. This is useful for mapping shortcuts to commands.
Example
emitKeypress({
keymap: [
{ sequence: '\x1B', shortcut: 'escape', command: 'cancel' },
{ sequence: '\x03', shortcut: 'ctrl+c', command: 'cancel' },
{ sequence: '\r', shortcut: 'return', command: 'submit' }
],
onKeypress: async (input, key, close) => {
// do stuff with keypress events
switch (key.command) {
case 'cancel':
console.log('canceled');
close();
break;
case 'submit':
console.log('submitted');
break;
}
}
});Pass a ReadableStream to input to listen for keypress events on the stream.
emitKeypress({
input: process.stdin,
onKeypress: async (input, key, close) => {
// do stuff with keypress events
console.log({ input, key });
if (key.shortcut === 'return' || key.shortcut === 'ctrl+c') {
close();
}
}
});Pass an onMousepress function to handle mouse events. When provided, mouse tracking is automatically enabled.
emitKeypress({
onKeypress: (input, key, close) => {
console.log('key:', key);
},
onMousepress: (mouse, close) => {
console.log('mouse:', mouse);
// mouse.x, mouse.y, mouse.button, mouse.action, etc.
}
});Enable bracketed paste mode to handle multi-line pastes as a single event.
emitKeypress({
enablePasteMode: true,
pasteModeTimeout: 100, // timeout in ms
maxPasteBuffer: 1024 * 1024, // 1MB limit
onKeypress: (input, key, close) => {
if (key.name === 'paste') {
console.log('pasted:', key.sequence);
}
}
});Enable the Kitty or modifyOtherKeys keyboard protocol for better key detection in supported terminals.
emitKeypress({
keyboardProtocol: true,
onKeypress: (input, key, close) => {
// Enhanced key reporting with better modifier detection
console.log({ input, key });
}
});Supported terminals include: kitty, alacritty, foot, ghostty, iterm, rio, wezterm (Kitty protocol), and windows_terminal, xterm, gnome_terminal, konsole, vscode, xfce4_terminal, mate_terminal, terminator (modifyOtherKeys protocol).
Control cursor visibility and get cursor position.
import { cursor, emitKeypress } from 'emit-keypress';
// Hide/show cursor
cursor.hide(process.stdout);
cursor.show(process.stdout);
// Or use the hideCursor option
emitKeypress({
hideCursor: true,
onKeypress: (input, key, close) => {
// cursor is automatically shown when close() is called
}
});
// Get initial cursor position
emitKeypress({
initialPosition: true,
onKeypress: (input, key, close) => {
if (key.name === 'position') {
console.log('cursor at:', key.x, key.y);
}
}
});| Option | Type | Default | Description |
|---|---|---|---|
input |
ReadStream |
process.stdin |
Input stream to listen on |
output |
WriteStream |
process.stdout |
Output stream for escape sequences |
keymap |
Array |
[] |
Custom key mappings |
onKeypress |
Function |
required | Keypress event handler |
onMousepress |
Function |
undefined |
Mouse event handler (enables mouse tracking) |
onExit |
Function |
undefined |
Called when the stream closes |
escapeCodeTimeout |
number |
500 |
Timeout for escape sequences (ms) |
handleClose |
boolean |
true |
Register cleanup on process exit |
hideCursor |
boolean |
false |
Hide cursor while listening |
initialPosition |
boolean |
false |
Request initial cursor position |
enablePasteMode |
boolean |
false |
Enable bracketed paste mode |
pasteModeTimeout |
number |
100 |
Paste mode timeout (ms) |
maxPasteBuffer |
number |
1048576 |
Max paste buffer size (bytes) |
keyboardProtocol |
boolean |
false |
Enable enhanced keyboard protocol |
Create an isolated instance with its own exit handlers.
import { createEmitKeypress } from 'emit-keypress';
const { emitKeypress, onExitHandlers } = createEmitKeypress({
setupProcessHandlers: true
});- Added
onMousepressoption for mouse event handling with automatic mouse tracking - Added bracketed paste mode support (
enablePasteMode,pasteModeTimeout,maxPasteBuffer) - Added enhanced keyboard protocol support for Kitty and modifyOtherKeys (
keyboardProtocoloption) - Added
createEmitKeypressfactory for creating isolated instances with separate exit handlers - Added
cursorutilities for hiding/showing cursor and getting position - Added
initialPositionoption to request cursor position on start - Added
hideCursoroption for automatic cursor visibility management - Added
onExitcallback option - Added CSI u (Kitty) and modifyOtherKeys protocol parsing for better modifier key detection
- Added
fnmodifier support to key objects - Added automatic terminal detection for keyboard protocol selection
- Added
keycodesexport with comprehensive key sequence mappings - Improved exit handler management with WeakMap-based session counting
- Improved cleanup: protocols are reset, mouse/paste modes disabled on close
Contributing
Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
Running Tests
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
$ npm install && npm testBuilding docs
(This project's readme.md is generated by verb, please don't edit the readme directly. Any changes to the readme must be made in the .verb.md readme template.)
To generate the readme, run the following command:
$ npm install -g verbose/verb#dev verb-generate-readme && verbJon Schlinkert
Copyright © 2025, Jon Schlinkert. Released under the MIT License.
This file was generated by verb-generate-readme, v0.8.0, on December 10, 2025.