The useHotkeys hook is the core of react-hotkeys-hook. It allows you to listen for keyboard shortcuts and execute callbacks when they are triggered.
Simple Hotkey
The most basic usage involves passing a key combination and a callback function:
import { useHotkeys } from 'react-hotkeys-hook';
function App() {
useHotkeys('a', () => {
console.log('Key A was pressed');
});
return <div>Press the 'A' key</div>;
}
Multiple Hotkeys
You can listen to multiple hotkeys with a single hook by separating them with commas:
useHotkeys('a, b, c', (event, hotkeysEvent) => {
console.log(`Key ${hotkeysEvent.keys?.[0]} was pressed`);
});
Alternatively, pass an array of keys:
useHotkeys(['a', 'b', 'c'], (event) => {
console.log('One of the keys was pressed');
});
Modifier Keys
Combine regular keys with modifiers like ctrl, shift, alt, or meta:
// Single modifier
useHotkeys('ctrl+s', () => {
console.log('Save action triggered');
});
// Multiple modifiers
useHotkeys('ctrl+shift+a', () => {
console.log('Complex shortcut triggered');
});
// Cross-platform modifier (cmd on Mac, ctrl on Windows/Linux)
useHotkeys('mod+s', () => {
console.log('Platform-aware save');
});
The mod key automatically maps to cmd on macOS and ctrl on other platforms, making your shortcuts work consistently across operating systems.
Keyboard Event and Hotkey Object
Your callback receives two arguments:
KeyboardEvent - The native browser keyboard event
HotkeysEvent - An object containing information about the triggered hotkey
useHotkeys('shift+a', (event, hotkeysEvent) => {
console.log('Keyboard Event:', event.key);
console.log('Hotkey Info:', hotkeysEvent);
// hotkeysEvent contains: { keys, shift, ctrl, alt, meta, mod, hotkey, ... }
});
The HotkeysEvent object structure:
{
keys: ['a'], // Array of non-modifier keys
shift: true, // Whether shift was pressed
ctrl: false, // Whether ctrl was pressed
alt: false, // Whether alt was pressed
meta: false, // Whether meta/cmd was pressed
mod: false, // Whether mod key was pressed
useKey: false, // Whether using produced key instead of code
isSequence: false, // Whether this is a key sequence
hotkey: 'shift+a' // The original hotkey string
}
Special Keys
The hook supports many special keys with convenient aliases:
// Arrow keys
useHotkeys('arrowup, arrowdown, arrowleft, arrowright', () => {
console.log('Arrow key pressed');
});
// Aliases for common keys
useHotkeys('esc', () => console.log('Escape'));
useHotkeys('return', () => console.log('Enter'));
useHotkeys('space', () => console.log('Space'));
// Function keys
useHotkeys('f1, f2, f3', () => {
console.log('Function key pressed');
});
// Other special keys
useHotkeys('backspace, tab, enter, delete', () => {
console.log('Special key pressed');
});
Wildcard Hotkey
Listen to all keyboard events using the wildcard *:
useHotkeys('*', (event) => {
console.log(`Any key pressed: ${event.key}`);
});
Use the wildcard sparingly as it triggers on every keypress, which can impact performance.
Preventing Default Behavior
Control whether the browser’s default behavior should be prevented:
// Always prevent default
useHotkeys('ctrl+s', (event) => {
console.log('Saving...');
}, { preventDefault: true });
// Conditionally prevent default
useHotkeys('enter', (event) => {
console.log('Enter pressed');
}, {
preventDefault: (event, hotkeysEvent) => {
// Only prevent if shift is not held
return !hotkeysEvent.shift;
}
});
Enabling and Disabling Hotkeys
Dynamically enable or disable hotkeys based on your application state:
function Editor() {
const [isEditing, setIsEditing] = useState(false);
// Only active when not editing
useHotkeys('ctrl+s', () => {
console.log('Quick save');
}, { enabled: !isEditing });
// Conditional enable with function
useHotkeys('esc', () => {
setIsEditing(false);
}, {
enabled: (event, hotkeysEvent) => {
return isEditing; // Only enabled while editing
}
});
return <div>...</div>;
}
Keydown and Keyup Events
By default, hotkeys trigger on keydown. You can change this behavior:
// Trigger on keyup only
useHotkeys('shift', () => {
console.log('Shift released');
}, { keyup: true });
// Trigger on both keydown and keyup
useHotkeys('a', () => {
console.log('Triggered twice per key press');
}, { keyup: true, keydown: true });
// Explicitly set keydown (default behavior)
useHotkeys('b', () => {
console.log('On key down');
}, { keydown: true });
By default, hotkeys are disabled when focus is inside form elements (inputs, textareas, etc.) to avoid interfering with typing:
// Default: disabled in form fields
useHotkeys('a', () => {
console.log('Will not trigger when typing in input');
});
// Enable for specific form tags
useHotkeys('ctrl+s', () => {
console.log('Works even in input fields');
}, { enableOnFormTags: ['input', 'textarea'] });
// Enable for all form tags
useHotkeys('esc', () => {
console.log('Works in all form elements');
}, { enableOnFormTags: true });
Form tags include: input, textarea, select, and ARIA roles like searchbox, textbox, spinbutton, etc.
Content Editable Elements
Similar to form tags, hotkeys are disabled in content-editable elements by default:
useHotkeys('ctrl+b', () => {
console.log('Bold text');
}, { enableOnContentEditable: true });
Dependencies Array
Use the dependencies array to control when the callback should be recreated, similar to useEffect:
function Counter() {
const [count, setCount] = useState(0);
// Without dependencies - always uses latest callback
useHotkeys('up', () => {
setCount(count + 1); // Always uses current count
});
// With empty dependencies - callback is fixed
useHotkeys('down', () => {
setCount(count - 1); // Uses count from first render only
}, []);
// With dependencies - callback updates when count changes
useHotkeys('r', () => {
console.log('Current count:', count);
}, [count]);
return <div>Count: {count}</div>;
}
Without a dependencies array, the callback always has access to the latest props and state. With an empty array [], the callback is “frozen” with values from the initial render.