Migrating from Mousetrap or hotkeys-js

Both are solid, long-running libraries — this isn't "they're bad, switch." It's an API mapping for when you specifically want first-class TypeScript, scopes, a React hook, or SSR-safety without writing the glue yourself, plus the one thing that doesn't carry over cleanly either way.

From Mousetrap

Mousetrap bind-keyboard
Mousetrap.bind('ctrl+a', fn) bindKeyboard.add('ctrl+a', fn)
Mousetrap.unbind('ctrl+a') bindKeyboard.remove('ctrl+a')
Mousetrap.bind('g i', fn) (sequence) bindKeyboard.add('g,i', fn) (comma, not space)
Mousetrap.reset() bindKeyboard.removeAll()
no built-in scopes { scope: 'modal' } + enableScope/disableScope
no built-in React hook useKeybind (bind-keyboard/react)

The one-argument shorthand carries over almost verbatim; the biggest real difference is the sequence separator (comma, not space) and that type is an explicit argument rather than inferred.

From hotkeys-js

hotkeys-js bind-keyboard
hotkeys('ctrl+a', fn) bindKeyboard.add('ctrl+a', fn)
hotkeys.unbind('ctrl+a') bindKeyboard.remove('ctrl+a')
hotkeys.setScope('modal') (one global scope) bindKeyboard.enableScope('modal') (several can be active at once)
hotkeys.filter = (event) => ... checkInputElements + per-binding allowInInputElements
no temporal sequences bindKeyboard.add('g,i', fn) — see the sequences recipe

The scope model is the real behavioral difference to know about: hotkeys-js has exactly one active scope at a time (switching to a new one deactivates the old), while bind-keyboard can have several scopes active simultaneously (each independently enabled/disabled), plus an always-on unscoped tier underneath all of them.

The one thing with no direct equivalent

hotkeys-js supports hotkeys('ctrl+a+s', fn) — a real simultaneous chord of two non-modifier keys held together, via its own tracked set of currently-held keys across separate keydown/keyup events. bind-keyboard resolves every combination from a single KeyboardEvent, which only ever carries one non-modifier key at a time (plus its four modifier flags) — so a string like "ctrl+a+s" throws a KeybindError here instead of silently mismatching. If you're migrating a binding like that, the closest equivalents are a sequence (if pressing them one after another is acceptable) or two separate bindings for whatever the actual intent was.

Related

Vim and Gmail-style key sequences · React keyboard shortcuts: the useKeybind hook · Keyboard shortcuts inside a modal