tanstack-hotkeys-guide
Purpose
TanStack Hotkeys is a type-safe, framework-agnostic library for handling keyboard shortcuts. It provides React hooks for registering hotkeys, multi-key sequences, recording custom shortcuts, tracking key state, and platform-aware display formatting. The library uses event.key as its primary API with event.code fallback for letter/digit keys. Currently in alpha -- API may change.
When NOT to use: For a single shortcut listener, a plain addEventListener('keydown', ...) may suffice. Reach for TanStack Hotkeys when you need multiple shortcuts, cross-platform Mod handling, sequences, recording, or key-state tracking.
Instructions
- Always use
Modinstead of platform-specificMetaorControlfor cross-platform shortcuts - Import from
@tanstack/react-hotkeysfor React projects (it re-exports everything from@tanstack/hotkeys) - Import from
@tanstack/hotkeysonly for vanilla JS without React - Prefer the string form (
'Mod+S') over RawHotkey objects unless the hotkey is dynamic or built programmatically - Always add
tabIndex={0}to elements used astargetrefs -- they must be focusable to receive keyboard events - When building shortcut customization UIs, combine
useHotkeyRecorderwithformatForDisplayfor recording and display - The recorder auto-converts platform keys to portable
Modformat -- do not manually convert recorded hotkeys - Do NOT combine
ModwithControlorModwithMeta-- these create duplicate modifiers on one platform - Avoid
Alt+lettershortcuts for cross-platform apps -- macOS produces special characters with Option+letter - Avoid
Shift+numberandShift+punctuationshortcuts -- results are keyboard-layout-dependent - Warn the user that TanStack Hotkeys is in alpha when recommending it for production use
- Default
preventDefault: trueandstopPropagation: trueare intentional -- override explicitly only when needed - Use
conflictBehavior: 'allow'for intentional duplicate hotkeys, not to silence bugs
Installation
npm install @tanstack/react-hotkeys
The React package re-exports everything from @tanstack/hotkeys. No separate core install needed. For vanilla JS only, install @tanstack/hotkeys directly.
Optional devtools:
npm install @tanstack/react-devtools @tanstack/react-hotkeys-devtools
Quick Start
import { useHotkey } from '@tanstack/react-hotkeys'
function App() {
useHotkey('Mod+S', () => saveDocument())
return <div>Press Cmd+S (Mac) or Ctrl+S (Windows) to save</div>
}
Mod resolves to Meta (Cmd) on macOS and Control on Windows/Linux.
React Hooks Reference
useHotkey(hotkey, callback, options?)
Register a keyboard shortcut. Auto-syncs callback every render (no stale closures). Auto-unregisters on unmount.
useHotkey('Mod+S', (event, { hotkey, parsedHotkey }) => {
save()
})
Accept a string ('Mod+S') or RawHotkey object ({ key: 'S', mod: true }).
useHotkeySequence(sequence, callback, options?)
Register Vim-style multi-key sequences. Each step can include modifiers.
useHotkeySequence(['G', 'G'], () => scrollToTop())
useHotkeySequence(['Mod+K', 'Mod+C'], () => commentSelection())
Options: { timeout: 1000, enabled: true }.
useHotkeyRecorder(options)
Record custom keyboard shortcuts for settings UIs. Auto-converts to portable Mod format.
const { isRecording, recordedHotkey, startRecording, stopRecording, cancelRecording } =
useHotkeyRecorder({ onRecord: (hotkey) => setShortcut(hotkey) })
Options: { onRecord, onCancel?, onClear? }. Escape cancels. Backspace/Delete clears.
useHeldKeys()
Return a reactive string[] of currently held key names.
useHeldKeyCodes()
Return a reactive Record<string, string> mapping key names to event.code values.
useKeyHold(key)
Return boolean for a specific key's hold state. Only re-renders when that key changes.
const isShiftHeld = useKeyHold('Shift')
useDefaultHotkeysOptions()
Return the current default options from HotkeysProvider context.
useHotkeysContext()
Return the full hotkeys context value, or null if outside a provider.
HotkeysProvider
Wrap the app to set global default options. Per-hook options override provider defaults.
import { HotkeysProvider } from '@tanstack/react-hotkeys'
<HotkeysProvider defaultOptions={{
hotkey: { preventDefault: true },
hotkeySequence: { timeout: 1500 },
hotkeyRecorder: { onCancel: () => console.log('cancelled') },
}}>
<App />
</HotkeysProvider>
Hotkey String Format
- Modifiers:
Control,Alt,Shift,Meta - Cross-platform:
Mod(Cmd on Mac, Ctrl on Windows/Linux) - Format:
Modifier+Modifier+Key-- e.g.,'Mod+Shift+S' - Single keys:
'Escape','Enter','F1','ArrowUp','A','1','/' - RawHotkey alternative:
{ key: 'S', mod: true, shift: true } Mod+ControlandMod+Metacombinations are NOT allowed (would duplicate modifiers)
useHotkey Options
| Option | Default | Description |
|---|---|---|
| enabled | true | Whether the hotkey is active |
| preventDefault | true | Call event.preventDefault() |
| stopPropagation | true | Call event.stopPropagation() |
| eventType | 'keydown' | 'keydown' or 'keyup' |
| requireReset | false | Fire only once per key press |
| ignoreInputs | smart | false for Mod+key and Escape; true for single keys and Shift/Alt combos |
| target | document | DOM element, document, window, or React ref |
| conflictBehavior | 'warn' | 'warn', 'error', 'replace', or 'allow' |
| platform | auto | Override: 'mac', 'windows', 'linux' |
Smart ignoreInputs default: Mod+key shortcuts and Escape fire in text inputs. Single keys and Shift/Alt combos are ignored. Button-type inputs (type="button/submit/reset") are NOT ignored -- shortcuts work on them.
Display Formatting
import { formatForDisplay, formatWithLabels, formatKeyForDebuggingDisplay } from '@tanstack/react-hotkeys'
formatForDisplay('Mod+S') // Mac: "⌘S" Windows: "Ctrl+S"
formatWithLabels('Mod+S') // Mac: "Cmd+S" Windows: "Ctrl+S"
formatKeyForDebuggingDisplay('Meta') // Mac: "⌘ Mod (Cmd)"
Options: { platform: 'mac' | 'windows' | 'linux' }.
Core Utilities (Vanilla JS)
Use these without React:
import {
parseHotkey, normalizeHotkey, validateHotkey,
createHotkeyHandler, createMultiHotkeyHandler, createSequenceMatcher,
getHotkeyManager, getKeyStateTracker, getSequenceManager,
} from '@tanstack/hotkeys'
parseHotkey('Mod+S')-- return ParsedHotkey objectnormalizeHotkey('cmd+s')-- return canonical form'Meta+S'validateHotkey('Alt+A')-- return{ valid, warnings, errors }createHotkeyHandler('Mod+S', callback)-- return event handler functioncreateMultiHotkeyHandler({ 'Mod+S': save, 'Mod+Z': undo })-- return single event handlercreateSequenceMatcher(['G', 'G'], { timeout: 500 })-- return{ match(), reset(), getProgress() }
Key Rules and Gotchas
- Library is in ALPHA -- API may change
Modis the recommended way to write cross-platform shortcuts- Default
preventDefault: trueandstopPropagation: true-- override explicitly if needed - When using
targetwith a ref, ensure the element hastabIndexfor focus - macOS may swallow keyup events for non-modifier keys when a modifier is held -- library handles this
- Window blur auto-clears all held keys to prevent "stuck" keys
conflictBehavior: 'warn'is default -- logs duplicates during developmentevent.keyis primary.event.codeis fallback for letter/digit keys whenevent.keyproduces special characters (macOS Option+letter, Shift+number)- Alt+letter on macOS may produce special characters.
validateHotkeywarns about this - Shift+number and Shift+punctuation produce layout-dependent characters (Shift+1 →
!) -- avoid these combos; use Mod+number instead - SSR:
detectPlatform()defaults to'linux'when navigator is undefined --Modresolves toControlduring server rendering - Canonical modifier order in normalized strings:
Control → Alt → Shift → Meta
Devtools Setup
import { TanStackDevtools } from '@tanstack/react-devtools'
import { hotkeysDevtoolsPlugin } from '@tanstack/react-hotkeys-devtools'
function App() {
return (
<div>
<TanStackDevtools plugins={[hotkeysDevtoolsPlugin()]} />
</div>
)
}
Devtools are no-op in production by default. Use /production import path for production debugging:
import { hotkeysDevtoolsPlugin } from '@tanstack/react-hotkeys-devtools/production'
Reference Files
references/api-reference.md-- Full API surface with types and interfaces. Read when you need exact type signatures, constructor parameters, or constant values.references/patterns.md-- 25 usage patterns with complete code examples. Read when generating component code, building UIs with hotkeys, or choosing between implementation approaches.references/troubleshooting.md-- Platform quirks, common issues, and solutions. Read when debugging hotkey issues, when the user reports unexpected behavior, or when working with macOS/SSR edge cases.