React keyboard shortcuts: the useKeybind hook
A component-scoped shortcut in one line — registered on mount, cleaned up on unmount, always calling your latest callback.
Install
react is a peer dependency of the separate
bind-keyboard/react entry point — the core
bind-keyboard import is completely unaffected either way,
so adding this never pulls React into a non-React project.
npm install bind-keyboard
Basic usage
useKeybind(keyCombination, callback, options?) creates its
own BindKeyboard instance inside a
useEffect and destroys it on cleanup — the component never
leaks a listener past its own lifetime.
import { useKeybind } from "bind-keyboard/react";
function SaveButton({ onSave }: { onSave: () => void }) {
useKeybind("cmdOrCtrl+s", (event) => {
event.preventDefault(); // stop the browser's own "Save page" dialog
onSave();
});
return <button onClick={onSave}>Save</button>;
}
No stale closures, no unnecessary re-subscribing
callback doesn't need to be stable across renders — the
latest one is always used (via a ref internally), without tearing down
and recreating the underlying binding just because an inline function
changed identity on every render. keyCombination and the
rest of options are compared by their
serialized content, not by reference, so passing a literal
array or object inline on every render (useKeybind(["ctrl+a", "ctrl+b"], cb)) never needlessly resubscribes either — only an actual change to what
they contain does.
Only while the component wants it: enabled
Pass enabled: false to unregister without unmounting —
useful for a shortcut that should only be live under some condition,
e.g. only while a specific tab is active:
useKeybind("cmdOrCtrl+f", openSearch, {
enabled: isActiveTab,
});
Everything .add() accepts, plus a few more
options accepts everything the core
.add() method's own options object does —
allowInInputElements, override,
description, scope — plus
preventRepeat/type (mirroring
.add()'s own positional arguments), enabled,
and the constructor options that matter per binding
(target, keyMode,
checkInputElements, debug). Each
useKeybind() call owns an independent
BindKeyboard instance, so there's no shared, app-wide
instance to configure elsewhere — see
scoping shortcuts inside a modal
for a pattern built entirely out of the scope option.
Why this is SSR-safe automatically
Registration only ever happens inside useEffect, which
React never runs during server rendering — so the DOM-touching
BindKeyboard instance underneath simply never gets created
on the server at all. There's no special SSR mode to opt into; it falls
out of how the hook is built. See
keyboard shortcuts in Next.js for the
framework-specific details (App Router's "use client",
Pages Router).