useKey
Bind keyboard shortcuts to handlers with scoped, document-level, or container-scoped listening. Supports single key, array of keys, or key map overloads.
useKey is the core binding primitive of @diffgazer/keys. It registers one or more hotkeys with the nearest KeyboardProvider and runs your handler when a matching combo fires. Bindings are scope-aware: the provider resolves which handler wins when several match, and a binding is automatically removed when its component unmounts.
When to use it
- Application shortcuts —
Cmd+Kto open a palette,Escapeto close an overlay,?to show help. - Component shortcuts that should only fire while a region has focus (pass
containerRef+focusWithinOnly). - Layered UI where a deeper surface (dialog, drawer) should shadow a shortcut owned by the page underneath — combine with
useScope.
Higher-level navigation hooks (useScopedNavigation, useFocusZone, useActionRowNavigation) are built on useKey; reach for it directly when you need an individual shortcut rather than list or zone movement.
Installation
Requires KeyboardProvider and the @diffgazer/keys package, which is not public on npm yet.
Diffgazer packages are not yet published to npm. Until the first release, pack @diffgazer/ui and @diffgazer/keys from the repository and install those tarballs.
@diffgazer/keys requires no CSS or Tailwind setup.
Parameters
The hotkey string uses the hotkey format: a key name with optional ctrl+, meta+, alt+, and shift+ modifiers (for example "meta+k", "shift+?", "Escape").
Returns
Behavior
Examples
Basic hotkey binding
Key map with multiple bindings
Container-scoped shortcut
Notes
Three overloads
useKey supports three call signatures: single key + handler, array of keys + handler, and a Record<string, KeyHandler> map (no separate handler argument).
Provider-dependent
useKey registers handlers when a KeyboardProvider is present. Without a provider it is a no-op.
Scope-aware
Handlers respect the scope stack managed by useScope. Deeper scopes take priority over shallower ones.
Declining a match
Return exactly false from a handler to let the next lower-priority matching handler in the active scope run. Other return values are ignored. When a handler with preventDefault enabled accepts the match (returns anything other than false), event.preventDefault() runs after that handler returns. A declining handler never prevents the default.
Edge cases
- No provider. Without a
KeyboardProviderancestor,useKeyis a no-op — it never throws, so a copied component degrades to "no shortcut" rather than crashing. scope: null. Passingnullskips registration entirely while keeping the Hook call in place, which is the supported way to disable a binding conditionally without violating the Rules of Hooks.- Key map overload.
useKey({ "ctrl+b": ..., Escape: ... })registers several combos at once; do not pass a separate handler argument with this form.
Related
useScope— push a named scope so deeper surfaces shadow page-level shortcuts.KeyboardProvider— the context that dispatches matched keys.- Hotkey format — modifier and key-name reference.
Source
Highlighted source loads after this disclosure opens. Browse the source repository.