emit-keypress

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 :heart: and support.
Install
Install with npm:
$ npm install --save emit-keypress
Why another keypress module?
Node'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.
Why use this?
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.
Usage
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();
}
});
API
onKeypress
Pass an onKeypress function to emitKeypress to handle keypress events.
const onKeypress = async (input, key, close) => {
console.log({ input, key });
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 });
setTimeout(() => {
close();
}, 10_000);
keymap
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) => {
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) => {
switch (key.command) {
case 'cancel':
console.log('canceled');
close();
break;
case 'submit':
console.log('submitted');
break;
}
}
});
input
Pass a ReadableStream to input to listen for keypress events on the stream.
emitKeypress({
input: process.stdin,
onKeypress: async (input, key, close) => {
console.log({ input, key });
if (key.shortcut === 'return' || key.shortcut === 'ctrl+c') {
close();
}
}
});
onMousepress
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);
}
});
Paste Mode
Enable bracketed paste mode to handle multi-line pastes as a single event.
emitKeypress({
enablePasteMode: true,
pasteModeTimeout: 100,
maxPasteBuffer: 1024 * 1024,
onKeypress: (input, key, close) => {
if (key.name === 'paste') {
console.log('pasted:', key.sequence);
}
}
});
Enhanced Keyboard Protocol
Enable the Kitty or modifyOtherKeys keyboard protocol for better key detection in supported terminals.
emitKeypress({
keyboardProtocol: true,
onKeypress: (input, key, close) => {
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).
Cursor Control
Control cursor visibility and get cursor position.
import { cursor, emitKeypress } from 'emit-keypress';
cursor.hide(process.stdout);
cursor.show(process.stdout);
emitKeypress({
hideCursor: true,
onKeypress: (input, key, close) => {
}
});
emitKeypress({
initialPosition: true,
onKeypress: (input, key, close) => {
if (key.name === 'position') {
console.log('cursor at:', key.x, key.y);
}
}
});
Options
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 |
createEmitKeypress
Create an isolated instance with its own exit handlers.
import { createEmitKeypress } from 'emit-keypress';
const { emitKeypress, onExitHandlers } = createEmitKeypress({
setupProcessHandlers: true
});
History
v2.0.0
- Added
onMousepress option 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 (
keyboardProtocol option)
- Added
createEmitKeypress factory for creating isolated instances with separate exit handlers
- Added
cursor utilities for hiding/showing cursor and getting position
- Added
initialPosition option to request cursor position on start
- Added
hideCursor option for automatic cursor visibility management
- Added
onExit callback option
- Added CSI u (Kitty) and modifyOtherKeys protocol parsing for better modifier key detection
- Added
fn modifier support to key objects
- Added automatic terminal detection for keyboard protocol selection
- Added
keycodes export 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
About
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 test
Building 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
Author
Jon Schlinkert
License
Copyright © 2025, Jon Schlinkert.
Released under the MIT License.
This file was generated by verb-generate-readme, v0.8.0, on December 10, 2025.