Don't have TanStack Hotkeys installed yet? See the Installation page for instructions.
The useHotkey hook is the primary way to register keyboard shortcuts in React:
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>
}
The Mod modifier automatically resolves to Meta (Command) on macOS and Control on Windows/Linux, so your shortcuts work across platforms without extra logic.
Register as many hotkeys as you need. Each useHotkey call is independent:
function Editor() {
useHotkey('Mod+S', () => save())
useHotkey('Mod+Z', () => undo())
useHotkey('Mod+Shift+Z', () => redo())
useHotkey('Mod+F', () => openSearch())
useHotkey('Escape', () => closeDialog())
return <div>Editor with keyboard shortcuts</div>
}
Attach hotkeys to specific elements instead of the entire document:
import { useRef } from 'react'
import { useHotkey } from '@tanstack/react-hotkeys'
function Panel() {
const panelRef = useRef<HTMLDivElement>(null)
// This hotkey only fires when the panel (or its children) has focus
useHotkey('Escape', () => closePanel(), { target: panelRef })
return (
<div ref={panelRef} tabIndex={0}>
<p>Press Escape while focused here to close</p>
</div>
)
}
Enable or disable hotkeys based on application state:
function Modal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
useHotkey('Escape', () => onClose(), { enabled: isOpen })
if (!isOpen) return null
return (
<div className="modal">
<p>Press Escape to close</p>
</div>
)
}
Register Vim-style key sequences with useHotkeySequence:
import { useHotkeySequence } from '@tanstack/react-hotkeys'
function VimStyleApp() {
useHotkeySequence(['G', 'G'], () => scrollToTop())
useHotkeySequence(['G', 'Shift+G'], () => scrollToBottom())
return <div>Use gg to go to top, gG to go to bottom</div>
}
Display modifier key state for power-user UIs:
import { useKeyHold, useHeldKeys } from '@tanstack/react-hotkeys'
function StatusBar() {
const isShiftHeld = useKeyHold('Shift')
const heldKeys = useHeldKeys()
return (
<div className="status-bar">
{isShiftHeld && <span>Shift mode active</span>}
{heldKeys.length > 0 && <span>Keys: {heldKeys.join('+')}</span>}
</div>
)
}
Format hotkeys for platform-aware display:
import { useHotkey, formatForDisplay } from '@tanstack/react-hotkeys'
function SaveButton() {
useHotkey('Mod+S', () => save())
return (
<button>
Save <kbd>{formatForDisplay('Mod+S')}</kbd>
{/* Mac: "⌘S" | Windows: "Ctrl+S" */}
</button>
)
}
Add the TanStack Devtools to your app to inspect registered hotkeys, view held keys, and test shortcuts:
import { TanStackDevtools } from '@tanstack/react-devtools'
import { hotkeysDevtoolsPlugin } from '@tanstack/react-hotkeys-devtools'
function App() {
return (
<div>
{/* Your app */}
<TanStackDevtools plugins={[hotkeysDevtoolsPlugin()]} />
</div>
)
}
Wrap your app with HotkeysProvider to set default options for all hotkey hooks globally. Any options passed directly to a hook will override the provider defaults.
import { HotkeysProvider } from '@tanstack/react-hotkeys'
function Root() {
return (
<HotkeysProvider
defaultOptions={{
hotkey: { preventDefault: true, ignoreInputs: true },
hotkeySequence: { timeout: 1500 },
hotkeyRecorder: { onCancel: () => console.log('Recording cancelled') },
}}
>
<App />
</HotkeysProvider>
)
}