useHotkey
Detects a keyboard combo and fires an action callback. Returns platform-aware visual representations of the keys for use in Shortcut or tooltips.
Overview
useHotkey registers a global keydown listener for a combination of keys and fires a callback when all specified keys are held simultaneously. It also returns ReactNode[] representations of the keys — with modifier keys rendered as SVG icons on macOS and as text labels on Windows — ready to pass to the Shortcut component.
useHotkey is used internally by Shortcut and ActionCard. You can also call it directly when you need keyboard listener logic without the visual badge.
Import
import { useHotkey } from '@unflow/ui/hooks/useHotkey';Parameters
| Param | Type | Default | Description |
|---|---|---|---|
keys | string[] | undefined | Keys in MDN KeyboardEvent.key format |
action | () => void | undefined | Callback to fire when the combo is pressed |
listen | boolean | false | Enables/disables the listener |
allowRepeat | boolean | false | Fire on key repeat (holding the key) |
Return value
| Field | Type | Description |
|---|---|---|
active | boolean | Whether all keys are currently held |
isHotkeyValid | boolean | Whether the config is complete and listening |
hotkey | ReactNode[] | Platform-rendered key labels |
Usage
Basic global shortcut
const { hotkey } = useHotkey({
keys: ['Meta', 'k'],
action: openSearch,
listen: true,
});
// Render the shortcut badge manually
return <div className="flex gap-1">{hotkey}</div>;Conditional listener
const { isHotkeyValid, hotkey } = useHotkey({
keys: selectedKeys,
action: performAction,
listen: isDialogOpen, // only active while dialog is open
});Check validity before rendering
isHotkeyValid is true only when listen is true, keys is non-empty, and action is defined. Use it to conditionally render the shortcut badge:
{isHotkeyValid && (
<div className="flex gap-0.5">{hotkey}</div>
)}Key naming
Keys follow MDN KeyboardEvent.key:
['Meta', 'k'] // ⌘K
['Control', 'Shift', 's'] // ⌃⇧S
['Alt', 'p'] // ⌥P (Mac) or Alt+P (Win)
['Enter'] // EnterPlatform awareness
| Key | macOS render | Windows render |
|---|---|---|
Meta | ⌘ SVG icon | Win |
Alt | ⌥ SVG icon | Alt |
Control | ⌃ SVG icon | Ctrl |
Shift | ⇧ SVG icon | Shift |
| Other | Uppercase text | Uppercase text |
Platform is detected via navigator.platform at runtime.
Blacklisted keys
The following keys are silently ignored and will result in isHotkeyValid: false:
- Arrow keys (
ArrowDown,ArrowUp,ArrowLeft,ArrowRight) Escape,(comma — used as a separator in some key string formats)
These conflict with focus management (arrows, Escape) and could cause unexpected behavior if registered globally.
Normalization
Keys are lowercased and sorted before registration. This means:
useHotkey({ keys: ['Control', 'K'] })
useHotkey({ keys: ['k', 'control'] })
// Both register the same comboImplementation notes
The hook attaches a single document.addEventListener('keydown', …) per combo. When multiple components call useHotkey with the same combo, each registers its own listener — there is no global deduplication. Use listen to ensure only one listener is active at a time when using the same combo in multiple places.