React hook for handling keyboard shortcuts in components in a declarative way
—
Utility function for checking if specified keys are currently pressed, enabling conditional logic based on keyboard state.
Function to check if specific keys are currently being pressed.
/**
* Check if specified keys are currently pressed
* @param key - Single key string or array of key strings to check
* @param delimiter - Character used to separate multiple keys in a string (default: ',')
* @returns True if all specified keys are currently pressed
*/
function isHotkeyPressed(key: string | readonly string[], delimiter?: string): boolean;Usage Examples:
import { isHotkeyPressed } from 'react-hotkeys-hook';
// Check single key
if (isHotkeyPressed('shift')) {
console.log('Shift is currently pressed');
}
// Check key combination (all keys must be pressed)
if (isHotkeyPressed('ctrl+k')) {
console.log('Ctrl+K is currently pressed');
}
// Check multiple possible combinations (any can be pressed)
if (isHotkeyPressed(['ctrl+k', 'cmd+k'])) {
console.log('Either Ctrl+K or Cmd+K is pressed');
}
// Check with custom delimiter
if (isHotkeyPressed('ctrl,shift,k', ',')) {
console.log('Ctrl, Shift, and K are all pressed');
}function ContextualHelpButton() {
const [showHelp, setShowHelp] = useState(false);
useEffect(() => {
const checkKeys = () => {
// Show help when F1 is pressed
if (isHotkeyPressed('f1')) {
setShowHelp(true);
} else {
setShowHelp(false);
}
};
// Check key state on each frame
const interval = setInterval(checkKeys, 16); // ~60fps
return () => clearInterval(interval);
}, []);
return (
<div>
{showHelp && (
<div className="help-overlay">
<p>Help is active while F1 is held</p>
</div>
)}
</div>
);
}function AdvancedSelector() {
const [selectedItems, setSelectedItems] = useState(new Set());
const handleItemClick = (itemId) => {
setSelectedItems(prev => {
const newSet = new Set(prev);
if (isHotkeyPressed('ctrl') || isHotkeyPressed('cmd')) {
// Multi-select: toggle item
if (newSet.has(itemId)) {
newSet.delete(itemId);
} else {
newSet.add(itemId);
}
} else if (isHotkeyPressed('shift')) {
// Range select: select from last to current
// Implementation for range selection
return handleRangeSelect(itemId, prev);
} else {
// Single select: replace selection
newSet.clear();
newSet.add(itemId);
}
return newSet;
});
};
return (
<div className="item-list">
{items.map(item => (
<div
key={item.id}
className={selectedItems.has(item.id) ? 'selected' : ''}
onClick={() => handleItemClick(item.id)}
>
{item.name}
</div>
))}
</div>
);
}function HotkeyReference() {
const [currentModifiers, setCurrentModifiers] = useState([]);
useEffect(() => {
const updateModifiers = () => {
const modifiers = [];
if (isHotkeyPressed('ctrl')) modifiers.push('Ctrl');
if (isHotkeyPressed('shift')) modifiers.push('Shift');
if (isHotkeyPressed('alt')) modifiers.push('Alt');
if (isHotkeyPressed('meta')) modifiers.push('Cmd');
setCurrentModifiers(modifiers);
};
const interval = setInterval(updateModifiers, 50);
return () => clearInterval(interval);
}, []);
const getHotkeyDescription = (baseKey, description) => {
const modifierStr = currentModifiers.length > 0
? currentModifiers.join('+') + '+'
: '';
return `${modifierStr}${baseKey} - ${description}`;
};
return (
<div className="hotkey-reference">
<h3>Available Hotkeys</h3>
{currentModifiers.length > 0 && (
<p>Currently holding: {currentModifiers.join(', ')}</p>
)}
<ul>
<li>{getHotkeyDescription('K', 'Open command palette')}</li>
<li>{getHotkeyDescription('S', 'Save document')}</li>
<li>{getHotkeyDescription('Z', 'Undo action')}</li>
</ul>
</div>
);
}function MovementController() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [isMoving, setIsMoving] = useState(false);
useEffect(() => {
const updateMovement = () => {
let deltaX = 0;
let deltaY = 0;
let moving = false;
// Basic movement
if (isHotkeyPressed('w') || isHotkeyPressed('arrowup')) {
deltaY -= 1;
moving = true;
}
if (isHotkeyPressed('s') || isHotkeyPressed('arrowdown')) {
deltaY += 1;
moving = true;
}
if (isHotkeyPressed('a') || isHotkeyPressed('arrowleft')) {
deltaX -= 1;
moving = true;
}
if (isHotkeyPressed('d') || isHotkeyPressed('arrowright')) {
deltaX += 1;
moving = true;
}
// Speed modifier
const speed = isHotkeyPressed('shift') ? 2 : 1;
if (moving) {
setPosition(prev => ({
x: prev.x + deltaX * speed,
y: prev.y + deltaY * speed
}));
}
setIsMoving(moving);
};
const interval = setInterval(updateMovement, 16); // 60fps
return () => clearInterval(interval);
}, []);
return (
<div className="movement-controller">
<div
className={`player ${isMoving ? 'moving' : ''}`}
style={{
transform: `translate(${position.x}px, ${position.y}px)`
}}
/>
<div className="status">
Position: ({position.x}, {position.y})
{isHotkeyPressed('shift') && ' (Fast mode)'}
</div>
</div>
);
}function CrossPlatformActions() {
const handleAction = (action) => {
// Check for platform-specific modifier keys
const cmdKey = isHotkeyPressed(['cmd', 'ctrl']); // Cmd on Mac, Ctrl elsewhere
const optionKey = isHotkeyPressed(['alt', 'option']); // Alt/Option
if (cmdKey && isHotkeyPressed('s')) {
handleSave();
} else if (cmdKey && isHotkeyPressed('z')) {
handleUndo();
} else if (cmdKey && optionKey && isHotkeyPressed('i')) {
handleDevTools();
}
};
return (
<div>
<p>Cross-platform shortcuts available:</p>
<ul>
<li>Cmd/Ctrl + S: Save</li>
<li>Cmd/Ctrl + Z: Undo</li>
<li>Cmd/Ctrl + Alt/Option + I: Dev Tools</li>
</ul>
</div>
);
}function KeyStateDebugger() {
const [pressedKeys, setPressedKeys] = useState([]);
useEffect(() => {
const commonKeys = [
'ctrl', 'shift', 'alt', 'meta', 'cmd',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'enter', 'escape', 'space', 'tab', 'backspace'
];
const checkKeys = () => {
const pressed = commonKeys.filter(key => isHotkeyPressed(key));
setPressedKeys(pressed);
};
const interval = setInterval(checkKeys, 50);
return () => clearInterval(interval);
}, []);
return (
<div className="key-debugger">
<h3>Currently Pressed Keys</h3>
<div className="pressed-keys">
{pressedKeys.length > 0 ? (
pressedKeys.map(key => (
<span key={key} className="key-badge">{key}</span>
))
) : (
<span className="no-keys">No keys pressed</span>
)}
</div>
<div className="combinations">
<h4>Active Combinations</h4>
{isHotkeyPressed('ctrl+k') && <div>Ctrl+K is active</div>}
{isHotkeyPressed('shift+ctrl') && <div>Shift+Ctrl is active</div>}
{isHotkeyPressed(['cmd+s', 'ctrl+s']) && <div>Save combination is active</div>}
</div>
</div>
);
}The function tracks key states using a global set that is automatically maintained:
keydown eventskeyup eventsAll keys are normalized to lowercase and common key codes are mapped to consistent names:
esc → escapereturn → enterleft → arrowleft, etc.Install with Tessl CLI
npx tessl i tessl/npm-react-hotkeys-hook