mrkeyoor.com_
Tue 22 Sept 22:33 UTC
npmWeb Frontendupdated 22 Sept 2026

react-hotkeys-hook review

react-hotkeys-hook 5.3.3 binds keyboard combinations and ordered sequences to React components. useHotkeys manages listener cleanup, callback dependencies, keydown or keyup selection, physical-code versus produced-character matching, and restrictions for focus, forms, editable elements, or a supplied Document. HotkeysProvider adds named application scopes and exposes registered descriptions; useRecordHotkeys captures user-defined bindings. Version 5 is ESM-only, while our Node 22 checks still loaded it through both import and require. The package handles event wiring, but shortcut discovery, conflict policy, and accessible alternatives remain UI responsibilities.

Verdict

react-hotkeys-hook 5.3.3 installed in 1.3 seconds with 0 audit findings, and our all-exports bundle measured 6.2 KB gzipped. It fits React apps that need lifecycle-bound shortcuts and scopes, provided the product still owns discoverability, conflicts, and accessible alternatives.

We installed it

Lab card: what happened when we installed react-hotkeys-hookScreenshot of react-hotkeys-hook documentation
Install✓ · 1.3s4 packages on disk · 8 MB
ImportESM import works · require() works · ESM package
Browser6.2 KBgzipped (16.8 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does react-hotkeys-hook install cleanly?

Yes. In a fresh container with an empty cache, npm install react-hotkeys-hook finished in 1 seconds, leaving 4 packages and 8 MB on disk. npm audit reported no known vulnerabilities.

How much does react-hotkeys-hook add to a browser bundle?

6.2 KB gzipped (16.8 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does react-hotkeys-hook work with both ESM and CommonJS?

Yes. Both import 'react-hotkeys-hook' and require('react-hotkeys-hook') worked in Node 22 in our run. The package is published as ESM.

Does react-hotkeys-hook include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

react-hotkeys-hook or hotkeys-js: which should you use?

hotkeys-js: Use it for imperative shortcuts outside React when your code can own binding and unbinding. react-hotkeys-hook 5.3.3 installed in 1.3 seconds with 0 audit findings, and our all-exports bundle measured 6.2 KB gzipped.

When should you not use react-hotkeys-hook?

Your application is not React. The public API is hooks plus context and declares React and ReactDOM as peers; hotkeys-js or tinykeys is a closer fit.

API stability4/5The four-part useHotkeys signature remains the center of version 5, with newer behavior added through typed options and companion hooks. The 5.0.0 line did break packaging and callback expectations by becoming ESM-only and standardizing the event pair. Scopes, sequences, custom documents, descriptions, and recording now form a larger contract, so another major upgrade deserves migration tests.
Docs5/5The README shows a basic handler, provider scopes, focus-bound refs, the complete function signature, and an option table with defaults. The documentation site returned HTTP 200 and adds live examples and API pages. Its changelog explains the ESM-only 5.0.0 break and the scope and sequence additions in 5.2.0. Modifier limits for sequences remain buried in source rather than the main API page.
Maintenance5/5npm published 5.3.3 on June 26, 2026, and GitHub records a push on August 26, 2026. The repository is unarchived, with 39 items in GitHub's combined issues-and-pull-requests counter. Version 5 work covers scope control, sequence matching, code-to-key normalization, shadow-DOM form detection, listener cleanup, and provider performance, so maintenance is recent and tied to browser behavior.
Ecosystem5/5npm counted 4,354,694 downloads for August 18 through 24, 2026, and GitHub reports 3,511 stars. React and ReactDOM >=16.8 are the only peer requirements, bundled declarations cover the public hooks, and our install found no direct dependencies. The surrounding ecosystem is intentionally React-only; plain DOM, Vue, and other frameworks need a different shortcut layer.

Use it if

  • React components need local shortcut listeners that follow mount and unmount and have bundled TypeScript types.
  • Dialogs, editors, and routes need named scopes so one key can be reused without every handler firing.
  • The product uses sequences, keyup handling, focus-bound bindings, iframe documents, or recorded user shortcuts.
  • A shortcut-help panel should read descriptions from the same registrations that power the handlers.
Skip it if

Setup reality

We installed react-hotkeys-hook 5.3.3 in 1.3 seconds in a fresh Node 22 Bookworm container. Four packages occupied 8 MB, and npm audit found 0 known vulnerabilities. The package lists no direct dependencies and 2 peers, React and ReactDOM >=16.8, with 56 KB unpacked and an MIT license. It ships types and declares ESM without an exports map. Both require() and ESM import worked in our test. The all-exports browser bundle was 16.8 KB minified and 6.2 KB gzipped.

A basic useHotkeys() call needs no provider. HotkeysProvider becomes necessary for named scopes or for reading registered shortcut descriptions. In React Server Components, the hook belongs in a client component. Listeners default to document; an iframe needs its own Document option. The focus-bound overload returns a callback ref, and the target must accept focus, often through tabIndex.

Inputs, textareas, selects, and contenteditable regions are ignored by default. That prevents shortcuts firing while someone types, but it also disables expected commands inside editors and search boxes until enableOnFormTags or enableOnContentEditable is set narrowly. Matching uses KeyboardEvent.code unless useKey is true. The mod alias means Command on macOS and Control elsewhere. preventDefault remains false by default and cannot cancel protected system shortcuts.

Sequences use > and expire after 1000 ms unless sequenceTimeoutMs changes. Current source notes that modifiers do not work inside sequences. Scopes control registration groups, not focus, so route and modal transitions must activate and deactivate them correctly. Keep handler dependencies current, render a visible shortcut reference, and provide clickable equivalents for users who cannot use the binding.

Patterns

Register a portable shortcut bind-basic-hotkey

import { useHotkeys } from 'react-hotkeys-hook';

export function SearchButton() {
  const [open, setOpen] = useState(false);
  useHotkeys('mod+k', () => setOpen(true));
  return <button onClick={() => setOpen(true)}>Search</button>;
}

`mod` resolves to Control on Windows and Linux and Meta on macOS. Mounting and unmounting the component controls listener lifetime.

Replace the browser Save action override-save-shortcut

useHotkeys(
  'mod+s',
  (event) => saveDocument(),
  { preventDefault: true, description: 'Save document' }
);

preventDefault starts false. Browsers and operating systems reserve some combinations, and the README names Meta+W as one that cannot be replaced.

Gate a handler on component state enable-conditionally

useHotkeys(
  'delete',
  () => deleteSelectedItem(selectedId),
  { enabled: Boolean(selectedId) },
  [selectedId]
);

Captured values follow useCallback dependency rules. enabled may also be a function that decides after inspecting the current keyboard event.

Assign alternatives to one action bind-multiple-hotkeys

useHotkeys('mod+enter, shift+enter', (event, hotkey) => {
  submit({ newWindow: hotkey.shift === true });
});

A comma separates alternatives and `+` joins keys by default. Change delimiter or splitKey when either character is itself part of a binding.

Allow commands while typing allow-form-shortcut

useHotkeys(
  'escape',
  () => clearSearch(),
  { enableOnFormTags: ['input', 'searchbox'] }
);

The hook ignores form controls initially. Opt in by tag or supported ARIA role, and enable contenteditable through its separate option.

Switch active shortcut groups control-hotkey-scopes

import { HotkeysProvider, useHotkeys, useHotkeysContext } from 'react-hotkeys-hook';

function Editor() {
  const { toggleScope } = useHotkeysContext();
  useHotkeys('mod+k', openCommandMenu, { scopes: ['editor'] });
  return <button onClick={() => toggleScope('editor')}>Toggle editor keys</button>;
}

export function App() {
  return <HotkeysProvider initiallyActiveScopes={['editor']}><Editor /></HotkeysProvider>;
}

HotkeysProvider is required for these controls. Activating a named scope replaces the wildcard scope instead of running alongside it.

Bind only inside a focused panel bind-to-focused-region

const ref = useHotkeys<HTMLDivElement>('mod+a', selectAllCards);

return (
  <div ref={ref} tabIndex={0} aria-label="Card grid">
    {cards.map(renderCard)}
  </div>
);

Attach the returned callback ref to the region. It must be focusable, and users need a visible indication of where keyboard focus currently sits.

Match a two-step sequence bind-key-sequence

useHotkeys('g>g', () => scrollToTop(), {
  sequenceTimeoutMs: 750,
  description: 'Go to top',
});

`>` separates steps and the window defaults to 1000 ms. The current implementation does not handle modifier keys within a sequence.

Fire after a key is released handle-key-release

useHotkeys(
  'space',
  () => setPanning(false),
  { keydown: false, keyup: true }
);

keydown is true and keyup is false by default. Enabling both allows calls in both phases, with the hook still applying its repeat handling.

Follow the active keyboard layout match-produced-character

useHotkeys('z', undo, { useKey: true });

Default matching uses physical KeyboardEvent.code. useKey switches to KeyboardEvent.key, which changes with layout, Shift, and some input methods.

Attach handlers to an iframe bind-iframe-document

const frameDocument = iframeRef.current?.contentDocument;

useHotkeys('mod+b', toggleBold, {
  document: frameDocument,
  enableOnContentEditable: true,
});

Browser policy permits contentDocument only for a same-origin frame. Wait for the frame to load before passing its Document to the hook.

Capture a custom binding record-user-hotkey

import { useRecordHotkeys } from 'react-hotkeys-hook';

const [keys, { start, stop, resetKeys, isRecording }] = useRecordHotkeys(false, ['escape']);

return (
  <>
    <button onClick={isRecording ? stop : start}>
      {isRecording ? 'Finish' : 'Record shortcut'}
    </button>
    <button onClick={resetKeys}>Clear</button>
    <output>{[...keys].join('+')}</output>
  </>
);

The recorder listens for document keydown and cancels defaults for accepted keys. Blacklisted keys retain their browser behavior and stay out of the result.

Alternatives

PackageRegistryPick it when
hotkeys-jsnpmUse it for imperative shortcuts outside React when your code can own binding and unbinding.
tinykeysnpmUse it for a small DOM combination listener without React scopes, providers, or recording hooks.
@mantine/hooksnpmUse it when Mantine is already installed and its simpler hook covers the required key combinations.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.