A high-performance React Native module built with Nitro Modules for capturing external keyboard keys and hardware button events. This library provides native-level performance for hardware key event handling with modern React Native architecture.
- High Performance: Built with Nitro Modules for native-level performance
- Key Event Capture: Capture external keyboard keys and hardware button events
- Real-time Monitoring: Listen to keyDown and keyUp events in real-time
- Key Information: Get detailed key information including keyCode, action, and pressed key
- Repeat Count: Track key repeat events for long press detection
- Modern Architecture: Uses Nitro Modules for better performance than bridge-based solutions
- Cross Platform: Works on both Android and iOS
- React Native v0.76.0 or higher
- Node 18.0.0 or higher
Important
This library uses Nitro Modules. Make sure to install react-native-nitro-modules as a dependency.
For Android, you need to manually implement the key event handlers in your MainActivity to capture hardware key events.
npm install react-native-nitro-keyevent react-native-nitro-modules
# or
yarn add react-native-nitro-keyevent react-native-nitro-modulesTo capture hardware key events on Android, you must override the key event methods in your MainActivity.java or MainActivity.kt:
import android.view.KeyEvent;
import com.margelo.nitro.keyevent.KeyEventManager;
public class MainActivity extends ReactActivity {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Forward key events to the KeyEvent module
KeyEventManager.getInstance().onKeyDown(keyCode, event);
// Option 1: Override default behavior (recommended for external keyboards)
super.onKeyDown(keyCode, event);
return true;
// Option 2: Keep default behavior (uncomment line below, comment lines above)
// return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Forward key events to the KeyEvent module
KeyEventManager.getInstance().onKeyUp(keyCode, event);
// Option 1: Override default behavior (recommended for external keyboards)
super.onKeyUp(keyCode, event);
return true;
// Option 2: Keep default behavior (uncomment line below, comment lines above)
// return super.onKeyUp(keyCode, event);
}
}import android.view.KeyEvent
import com.margelo.nitro.keyevent.KeyEventManager
class MainActivity : ReactActivity() {
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
// Forward key events to the KeyEvent module
KeyEventManager.getInstance().onKeyDown(keyCode, event)
// Option 1: Override default behavior (recommended for external keyboards)
super.onKeyDown(keyCode, event)
return true
// Option 2: Keep default behavior (uncomment line below, comment lines above)
// return super.onKeyDown(keyCode, event)
}
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
// Forward key events to the KeyEvent module
KeyEventManager.getInstance().onKeyUp(keyCode, event)
// Option 1: Override default behavior (recommended for external keyboards)
super.onKeyUp(keyCode, event)
return true
// Option 2: Keep default behavior (uncomment line below, comment lines above)
// return super.onKeyUp(keyCode, event)
}
}If you want to prevent multiple events when a key is held down, modify the onKeyDown method:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Only forward the first event, ignore repeats
if (event.getRepeatCount() == 0) {
KeyEventManager.getInstance().onKeyDown(keyCode, event);
}
super.onKeyDown(keyCode, event);
return true;
}iOS support is currently in development. The module structure is ready for iOS implementation.
import {
onKeyDownListener,
onKeyUpListener,
removeKeyDownListener,
removeKeyUpListener,
type KeyEventData
} from 'react-native-nitro-keyevent';
const MyComponent = () => {
useEffect(() => {
// Listen for key down events
onKeyDownListener((keyEvent: KeyEventData) => {
console.log('Key Down:', {
keyCode: keyEvent.keyCode,
action: keyEvent.action,
pressedKey: keyEvent.pressedKey,
repeatCount: keyEvent.repeatcount || 0
});
});
// Listen for key up events
onKeyUpListener((keyEvent: KeyEventData) => {
console.log('Key Up:', {
keyCode: keyEvent.keyCode,
action: keyEvent.action,
pressedKey: keyEvent.pressedKey
});
});
// Cleanup listeners on unmount
return () => {
removeKeyDownListener();
removeKeyUpListener();
};
}, []);
return (
<View>
<Text>Press any hardware key to see events in console</Text>
</View>
);
};The library exports simple functions for handling keyboard and hardware key events.
Registers a listener for key down events. This is triggered when a hardware key is pressed down.
Parameters:
callback: (event: KeyEventData) => void- Function to call when a key down event occurs
Example:
import { onKeyDownListener } from 'react-native-nitro-keyevent';
onKeyDownListener((keyEvent) => {
console.log('Key pressed:', keyEvent.pressedKey)
console.log('Key code:', keyEvent.keyCode)
console.log('Repeat count:', keyEvent.repeatcount)
})Registers a listener for key up events. This is triggered when a hardware key is released.
Parameters:
callback: (event: KeyEventData) => void- Function to call when a key up event occurs
Example:
import { onKeyUpListener } from 'react-native-nitro-keyevent';
onKeyUpListener((keyEvent) => {
console.log('Key released:', keyEvent.pressedKey)
console.log('Key code:', keyEvent.keyCode)
})Removes the current key down event listener.
Example:
import { removeKeyDownListener } from 'react-native-nitro-keyevent';
removeKeyDownListener()Removes the current key up event listener.
Example:
import { removeKeyUpListener } from 'react-native-nitro-keyevent';
removeKeyUpListener()The data structure passed to event listeners containing key information:
interface KeyEventData {
keyCode: number // The hardware key code
action: number // The key action (down/up)
pressedKey: string // The character representation of the key
repeatcount?: number // Number of times key has repeated (only for keyDown)
characters?: string // Multiple characters (for special cases)
}| Key | Key Code | Description |
|---|---|---|
| Volume Up | 24 | Volume up button |
| Volume Down | 25 | Volume down button |
| Power | 26 | Power button |
| Back | 4 | Back button |
| Home | 3 | Home button |
| Menu | 82 | Menu button |
| Space | 62 | Space bar |
| Enter | 66 | Enter/Return key |
| Del | 67 | Delete/Backspace key |
For a complete list of Android key codes, see Android KeyEvent Documentation.
import { onKeyDownListener, removeKeyDownListener } from 'react-native-nitro-keyevent';
const VolumeController = () => {
useEffect(() => {
onKeyDownListener((keyEvent) => {
switch (keyEvent.keyCode) {
case 24: // Volume Up
console.log('Volume Up pressed');
// Custom volume up handling
break;
case 25: // Volume Down
console.log('Volume Down pressed');
// Custom volume down handling
break;
}
});
return () => {
removeKeyDownListener();
};
}, []);
return (
<View>
<Text>Use volume buttons to control app</Text>
</View>
);
};import {
onKeyDownListener,
onKeyUpListener,
removeKeyDownListener,
removeKeyUpListener
} from 'react-native-nitro-keyevent';
const ExternalKeyboardHandler = () => {
const [pressedKeys, setPressedKeys] = useState<string[]>([]);
useEffect(() => {
onKeyDownListener((keyEvent) => {
// Handle arrow keys for navigation
switch (keyEvent.keyCode) {
case 19: // DPAD_UP
console.log('Navigate up');
break;
case 20: // DPAD_DOWN
console.log('Navigate down');
break;
case 21: // DPAD_LEFT
console.log('Navigate left');
break;
case 22: // DPAD_RIGHT
console.log('Navigate right');
break;
case 66: // ENTER
console.log('Select/Enter pressed');
break;
default:
// Add pressed key to list
setPressedKeys(prev => [...prev, keyEvent.pressedKey]);
break;
}
});
onKeyUpListener((keyEvent) => {
// Remove key from pressed keys list
setPressedKeys(prev =>
prev.filter(key => key !== keyEvent.pressedKey)
);
});
return () => {
removeKeyDownListener();
removeKeyUpListener();
};
}, []);
return (
<View>
<Text>Pressed Keys: {pressedKeys.join(', ')}</Text>
<Text>Use external keyboard for navigation</Text>
</View>
);
};import {
onKeyDownListener,
onKeyUpListener,
removeKeyDownListener,
removeKeyUpListener
} from 'react-native-nitro-keyevent';
const GameController = () => {
const [gameState, setGameState] = useState({
isMoving: false,
direction: null,
isJumping: false
});
useEffect(() => {
onKeyDownListener((keyEvent) => {
// Prevent multiple events on key repeat for jump action
if (keyEvent.repeatcount && keyEvent.repeatcount > 0) {
return; // Ignore repeated events
}
switch (keyEvent.keyCode) {
case 62: // SPACE - Jump
setGameState(prev => ({ ...prev, isJumping: true }));
setTimeout(() => {
setGameState(prev => ({ ...prev, isJumping: false }));
}, 500);
break;
case 19: // DPAD_UP
setGameState(prev => ({
...prev,
isMoving: true,
direction: 'up'
}));
break;
case 20: // DPAD_DOWN
setGameState(prev => ({
...prev,
isMoving: true,
direction: 'down'
}));
break;
case 21: // DPAD_LEFT
setGameState(prev => ({
...prev,
isMoving: true,
direction: 'left'
}));
break;
case 22: // DPAD_RIGHT
setGameState(prev => ({
...prev,
isMoving: true,
direction: 'right'
}));
break;
}
});
onKeyUpListener((keyEvent) => {
// Stop movement when key is released
switch (keyEvent.keyCode) {
case 19: // DPAD_UP
case 20: // DPAD_DOWN
case 21: // DPAD_LEFT
case 22: // DPAD_RIGHT
setGameState(prev => ({
...prev,
isMoving: false,
direction: null
}));
break;
}
});
return () => {
removeKeyDownListener();
removeKeyUpListener();
};
}, []);
return (
<View style={styles.gameContainer}>
<Text>Game State:</Text>
<Text>Moving: {gameState.isMoving ? 'Yes' : 'No'}</Text>
<Text>Direction: {gameState.direction || 'None'}</Text>
<Text>Jumping: {gameState.isJumping ? 'Yes' : 'No'}</Text>
</View>
);
};This library captures physical hardware key events from:
- External Keyboards: USB, Bluetooth keyboards
- Hardware Buttons: Volume buttons, power button, back button
- Gaming Controllers: Bluetooth game controllers with D-pad
- Remote Controls: TV remote controls and media devices
- Custom Hardware: Any device that sends Android KeyEvent
- Hardware Key Press → Android KeyEvent generated
- MainActivity Handler → Forwards to KeyEventManager
- KeyEventManager → Notifies registered listeners
- NitroKeyEvent Module → Processes and formats event data
- React Native Bridge → Sends event to JavaScript
- KeyEvent Listeners → Your callback functions receive the event
- ✅ Full support for all hardware key events
- ✅ Key repeat detection
- ✅ Custom key event processing
- 🚧 In development
- 🚧 External keyboard support planned
- 🚧 Hardware button support planned
- Check MainActivity Setup: Ensure you've added the key event handlers
- Verify Import: Make sure
KeyEventManageris imported correctly - Test with External Keyboard: Some built-in keys may be handled by the system
- Check Logs: Enable React Native debugging to see if events are being generated
- Remove Listeners: Always remove listeners in cleanup to prevent memory leaks
- Debounce Events: For high-frequency events, consider debouncing in your handlers
- Key Repeat: Handle
repeatcountappropriately for long-press scenarios
When changing spec files (*.nitro.ts), please re-run nitrogen:
npx nitro-codegen- Clone the repository
- Install dependencies:
npm install - Set up the example project
- Run codegen after spec changes:
npx nitro-codegen
When adding new key event features:
- Update the
NitroKeyEvent.nitro.tsspec file - Run
npx nitro-codegento generate native interfaces - Implement the native Android code in
NitroKeyEvent.kt - Update the
KeyEventclass in TypeScript - Add tests and update documentation
See the contributing guide to learn how to contribute to the repository and the development workflow.
android/: Android native implementation (Kotlin, C++)NitroKeyEvent.kt: Main Android implementationKeyEventManager.kt: Key event manager singletonKeyEventListeners.kt: Event listener interface
ios/: iOS native implementation (Swift, C++) - In developmentnitrogen/: Auto-generated files by Nitro Modulessrc/: TypeScript source code and API definitionsindex.ts: Main export file with KeyEvent classspecs/NitroKeyEvent.nitro.ts: Nitro module specification
nitro.json: Nitro Module configurationpackage.json: Package metadata and dependencies
Special thanks to the following open-source projects which inspired and supported the development of this library:
- mrousavy/nitro – for the Nitro Modules architecture and tooling
- kevinejohn/react-native-keyevent – for the original idea of handling hardware key events in React Native
MIT © Thành Công