useHotkey
Bind a keyboard shortcut to a callback.
API
function useHotkey(
keys: string | string[],
listener: (event: KeyboardEvent) => void,
options?: {
enabled?: boolean;
target?: EventTarget | React.RefObject<EventTarget | null>;
eventName?: "keydown" | "keyup";
eventField?: "key" | "code";
repeatable?: boolean;
allowInEditable?: boolean;
preventDefault?: boolean;
},
): void;
Parameters:
keys: A hotkey string, or an array of hotkey strings that all trigger the listener. A hotkey string consists of modifiers joined with+followed by a single key, for example"mod+shift+k". Supported modifiers arectrl(control),alt(option),shift,meta(cmd,command) andmod. The key is aKeyboardEvent.keyvalue (or aKeyboardEvent.codename whenoptions.eventFieldis"code") and is case-insensitive; the aliasesesc,space,plus,up,down,leftandrightare also accepted.listener: The callback invoked when a hotkey is pressed; it receives the keyboard event.options: Options object.options.enabled: Whether the hotkey is active. Default istrue.options.target: The event target that receives the listener: an element,window,document, or a ref of them. Default isdocument.options.eventName: The keyboard event to listen for. Default is"keydown".options.eventField: TheKeyboardEventfield the key token is compared against:"key"(the produced character) or"code"(the physical key position). Default is"key".options.repeatable: Whether to fire again on key auto-repeat while the key is held down. Default isfalse.options.allowInEditable: Whether to fire while the focus is in an editable element, such as a text input, a textarea, a select or a content-editable element. Default isfalse.options.preventDefault: Whether to callpreventDefault()on the matched event. Default isfalse.
Notes
- Modifiers must match exactly: pressing
kwith both Control and Meta held does not fire a"ctrl+k"hotkey. modresolves to Command on Apple platforms and to Control everywhere else, so"mod+k"works on every operating system.- When
targetis a ref that is not assigned yet, no listener is attached: the hotkey never falls back todocument. - Inputs that do not accept text, such as checkboxes, radio buttons and range inputs, do not block hotkeys, so shortcuts keep working while they are focused.
- A
targetthat is itself editable is still filtered byallowInEditable: binding a shortcut to a text field needsallowInEditable: true. - On macOS, Option changes the reported character (
Option+Cproducesç), soaltshortcuts must useeventField: "code"to match; prefer letter keys withmodorctrlfor cross-platform shortcuts. - With
eventField: "code", key tokens areKeyboardEvent.codenames: letters and digits are converted automatically (abecomesKeyA,1becomesDigit1), while other keys are written as their code, such ascomma,equalandarrowup. - The
+key is written asplus, and because+requires Shift on most layouts, bind it asshift+plus. WitheventField: "code"use the physical name instead, such asshift+equal. - Keystrokes produced during an IME composition are ignored.
- Sequences such as
g iand scopes are not supported. Call the hook once per shortcut and useenabledto turn a group of shortcuts on and off together.
Example
The following example counts how many times the shortcut is pressed.
Press Ctrl + A (or ⌘ + A) to count: 0
import { useState } from "react";
import { useHotkey } from "@lilib/hooks";
function Example() {
const [count, setCount] = useState(0);
useHotkey(
"mod+a",
() => {
setCount((value) => value + 1);
},
{ preventDefault: true },
);
return (
<>
Press <kbd>Ctrl</kbd> + <kbd>A</kbd> (or <kbd>⌘</kbd> + <kbd>A</kbd>) to count: {count}
</>
);
}
export default Example;