# Build a Hotkeys / Keyboard Shortcuts System

Source: https://www.techinterview.org/post/3233475214/build-hotkeys-keyboard-shortcuts/
Updated: 2026-07-26 · techinterview.org

Power users expect keyboard shortcuts. [Linear](/companies/linear/) has hundreds; Notion many; Gmail famously does. The interview tests whether you understand the registration patterns, scope handling, and conflict resolution.

## Functional requirements

- Register shortcuts globally and per-component

- Detect modifier keys (Cmd, Ctrl, Shift, Alt)

- Handle key combinations (Cmd+Shift+K)

- Don't fire when user is typing in an input

- Show shortcut hints (next to menu items, in command palette)

- Help dialog showing all shortcuts (?)

## Architecture

Two pieces:

- Global event listener on keydown — one listener attached at the document or window level so shortcuts work no matter what's focused. Attaching a listener per component instead leads to duplicate handlers and events that get missed depending on focus.

- Registry of shortcut → handler mappings — a central map keyed by a normalized combo (like `cmd+k`) that the single listener consults on each keypress. Centralizing it lets you detect duplicate combos, generate the help dialog from one source, and enable or disable groups by scope.

## Library: react-hotkeys-hook

The standard React option:


```
useHotkeys('cmd+k', (e) => {
  e.preventDefault();
  openCommandPalette();
});
```


Cross-platform: `cmd` maps to Cmd on Mac, Ctrl on Windows/Linux.

## Detecting modifier keys

Cross-platform detection:

- Mac: `e.metaKey` for Cmd

- Windows/Linux: `e.ctrlKey` for Ctrl

- Convention: "Cmd/Ctrl" handled by libraries automatically

## Don't fire in inputs

Critical UX: typing "k" in an input should not trigger Cmd+K shortcut's "k" component.

Check event target:


```
if (['INPUT', 'TEXTAREA'].includes(e.target.tagName) || e.target.isContentEditable) {
  return; // ignore
}
```


Exception: shortcuts with modifiers (Cmd+S, Cmd+Z) should fire even in inputs.

## Scopes

Different shortcuts in different parts of the app:

- Global: always active (Cmd+K, Esc) — these fire regardless of focus, so keep the set small and stable to avoid surprising the user.

- Modal: only when modal is open — bind these when the modal mounts and unbind on close, so Esc closes the modal without also firing a global handler underneath it.

- Editor: only when editor focused — activate the editor's shortcut set on focus and tear it down on blur, otherwise formatting keys like Cmd+B leak into the rest of the app.

Scope-aware libraries enable/disable based on focus.

## Conflict resolution

Two shortcuts on the same key. Strategies:

- Most-recently-registered wins — a simple stack where the last handler bound for a key shadows earlier ones; predictable, but a stale registration you forgot to unbind can quietly hide a global shortcut.

- Most-specific scope wins (modal beats global) — resolve by scope depth so the innermost active context handles the key first, which matches what users expect (Esc closes the open dialog, not the page).

- Explicit priority numbers — attach a priority to each binding and sort on it; the most flexible option, but you have to keep the numbers coherent as the app grows.

## Sequence shortcuts

Some apps support multi-key sequences (Vim-style):

- "g, i" → go to inbox

- "g, p" → go to projects

Implementation: track previous key with timeout (~1 second).

## Display

Show shortcut hints visibly:

- Next to menu items: "Edit (E)"

- In tooltips: "Save (Cmd+S)"

- In command palette results

Use `<kbd>` for visual styling.

## Help dialog

"?" key (or Cmd+/) opens a dialog listing all shortcuts. Standard UX:

- Grouped by category (Navigation, Actions, Editing)

- Each row: shortcut + description

- Search to filter

## Accessibility

- Don't conflict with browser shortcuts (don't override Cmd+T, Cmd+W) — you often can't reliably preventDefault these anyway, and hijacking them breaks the user's muscle memory. Interviewers like to hear you name the reserved combos you deliberately avoid.

- Don't conflict with screen-reader shortcuts — screen readers reserve many keys (arrows, single letters in browse mode); single-key shortcuts are the worst offenders, so prefer modified combos or scope single keys tightly.

- Provide alternative interactions (mouse-clickable equivalent) — every shortcut should map to a visible control, so the action stays discoverable and usable without a keyboard.

- Document all shortcuts — surface them in the help dialog and in tooltips so keyboard-only and assistive-tech users can actually find them.

## Common antipatterns

- Shortcut fires while typing in input — the most common bug; guard on the event target's tag name and `isContentEditable` before dispatching.

- Different platforms have different shortcuts (mismatched Cmd/Ctrl) — hardcoding `metaKey` breaks Windows users; normalize to a "mod" key that maps to Cmd on Mac and Ctrl elsewhere.

- No way for users to discover shortcuts — without hints and a help dialog, only power users who read docs benefit; show the combo next to the menu item that triggers it.

- Conflicting shortcuts (two handlers for same key) — pick a resolution strategy up front instead of relying on registration order by accident.

## Frequently Asked Questions

### Should I support customizable shortcuts?

For power-user apps (IDEs, design tools): yes. For typical SaaS: defaults are usually enough.

### How do I handle international keyboards?

Use `e.code` (physical key) instead of `e.key` for layout-independence. Trade-off: French AZERTY users get the same physical-key shortcuts as US QWERTY, even if labels differ.

### Can shortcuts work on mobile?

Bluetooth keyboards yes. On-screen keyboards: no — there are no Cmd/Ctrl keys. Provide button alternatives.
