useKeyboard
A React hook to handle keyboard shortcuts and keypress events in a declarative way.
✅ Features
- Easily bind keyboard events globally or to a specific element
- Supports multiple keys and key combinations (e.g., ctrl+s)
- Works in client-side only environments
- Fully typed with TypeScript
📦 Usage
1import { useKeyboard } from './useKeyboard' 2 3useKeyboard('ctrl+s', (e) => { 4 e.preventDefault() 5 console.log('Save triggered!') 6}) 7 8useKeyboard(['ArrowUp', 'ArrowDown'], (e) => { 9 console.log('Arrow key pressed:', e.key) 10})
📋 Source Code
This is the full implementation of useKeyboard
1import { useEffect } from "react";
2
3type KeyCombo = string | string[];
4
5interface UseKeyboardOptions {
6 /**
7 * The key or key combo(s) to listen for.
8 * Example: 'Escape' or ['Control', 'k']
9 */
10 keys: KeyCombo;
11
12 /**
13 * The callback function to run when the key(s) are pressed.
14 */
15 callback: (event: KeyboardEvent) => void;
16
17 /**
18 * Listen on 'keydown' or 'keyup'. Defaults to 'keydown'.
19 */
20 eventType?: "keydown" | "keyup";
21
22 /**
23 * Whether the listener should be global (on window) or scoped to an element.
24 */
25 target?: HTMLElement | null;
26
27 /**
28 * Set to true if you want the event to trigger only once until released.
29 */
30 once?: boolean;
31
32 /**
33 * Optional: Whether to prevent the default action on key press.
34 */
35 preventDefault?: boolean;
36}
37
38export function useKeyboard({
39 keys,
40 callback,
41 eventType = "keydown",
42 target = typeof window !== "undefined" ? window : null,
43 once = false,
44 preventDefault = false,
45}: UseKeyboardOptions) {
46 useEffect(() => {
47 if (!target) return;
48
49 const combo = Array.isArray(keys) ? keys.map(k => k.toLowerCase()) : [keys.toLowerCase()];
50
51 const handler = (event: KeyboardEvent) => {
52 const pressedKey = event.key.toLowerCase();
53
54 // If it's a multi-key combo, check all keys are pressed
55 const comboMatch = combo.every(key => {
56 switch (key) {
57 case "ctrl":
58 case "control":
59 return event.ctrlKey;
60 case "shift":
61 return event.shiftKey;
62 case "alt":
63 return event.altKey;
64 case "meta":
65 case "cmd":
66 case "command":
67 return event.metaKey;
68 default:
69 return key === pressedKey;
70 }
71 });
72
73 if (comboMatch) {
74 if (preventDefault) event.preventDefault();
75 callback(event);
76 if (once) target.removeEventListener(eventType, handler);
77 }
78 };
79
80 target.addEventListener(eventType, handler);
81 return () => {
82 target.removeEventListener(eventType, handler);
83 };
84 }, [keys, callback, eventType, target, once, preventDefault]);
85}
86