Skip to main content

Quick Start

This guide will help you get up and running with react-hotkeys-hook quickly. We’ll cover the most common use cases with real examples.

Basic Usage

The simplest way to use the hook is to pass a key combination and a callback function:
import { useHotkeys } from 'react-hotkeys-hook'
import { useState } from 'react'

export const Counter = () => {
  const [count, setCount] = useState(0)
  
  useHotkeys('ctrl+k', () => setCount(count + 1), [count])

  return (
    <p>
      Pressed {count} times.
    </p>
  )
}
Notice the dependency array [count] at the end - just like useCallback, you need to include any variables from your component that the callback uses.

Key Combinations

Single Keys

Listen to individual keys:
useHotkeys('a', () => console.log('Pressed A'))
useHotkeys('esc', () => console.log('Pressed Escape'))
useHotkeys('space', () => console.log('Pressed Space'))

Modifier Keys

Combine keys with modifiers like ctrl, shift, alt, and meta:
useHotkeys('ctrl+s', () => console.log('Save'))
useHotkeys('meta+shift+a', () => console.log('Select all (Mac)'))
useHotkeys('alt+b', () => console.log('Bold'))
Use mod instead of meta or ctrl to automatically use Cmd on Mac and Ctrl on Windows/Linux:
useHotkeys('mod+s', () => console.log('Save')) // Cmd+S on Mac, Ctrl+S elsewhere

Multiple Hotkeys

Listen to multiple key combinations with the same callback:
// Using an array
useHotkeys(['ctrl+k', 'ctrl+p'], () => console.log('Command palette'))

// Or using a comma-separated string
useHotkeys('ctrl+k, ctrl+p', () => console.log('Command palette'))

Key Sequences

Create Vim-style key sequences:
useHotkeys('y>e>e>t', () => console.log('Yeet!'))
Sequences use > by default to separate keys. You have 1000ms between keypresses before the sequence resets.

Working with Options

The hook accepts an options object to customize behavior:

Prevent Default Behavior

Prevent the browser’s default action for a key combination:
useHotkeys('meta+s', () => {
  console.log('Saving...')
  // Your save logic here
}, { preventDefault: true })

Enable on Form Fields

By default, hotkeys are disabled when typing in input fields. Enable them if needed:
// Enable on all form fields
useHotkeys('ctrl+enter', () => console.log('Submit'), { enableOnFormTags: true })

// Enable only on specific form tags
useHotkeys('ctrl+enter', () => console.log('Submit'), { 
  enableOnFormTags: ['textarea'] 
})

Key Up vs Key Down

// Trigger on key release instead of key press
useHotkeys('shift', () => console.log('Shift released'), { keyup: true })

// Trigger on both key down and key up
useHotkeys('a', () => console.log('Key event'), { 
  keyup: true, 
  keydown: true 
})

Conditional Enabling

Enable or disable hotkeys based on state:
const [isEnabled, setIsEnabled] = useState(true)

useHotkeys('ctrl+k', () => console.log('Triggered'), { 
  enabled: isEnabled 
})

Focus Trapping

Limit hotkeys to a specific element by using the returned ref:
import { useHotkeys } from 'react-hotkeys-hook'
import { useState } from 'react'

export const FocusedCounter = () => {
  const [count, setCount] = useState(0)
  const ref = useHotkeys<HTMLDivElement>(
    'ctrl+k', 
    () => setCount(prevCount => prevCount + 1)
  )

  return (
    <div ref={ref} tabIndex={-1} style={{ padding: '20px', border: '2px solid blue' }}>
      Click here and press Ctrl+K. Count: {count}
    </div>
  )
}
The element must be focusable for this to work. Add tabIndex={-1} or tabIndex={0} to make an element focusable.

Using Scopes

Scopes let you organize hotkeys and enable/disable groups of shortcuts:
import { HotkeysProvider, useHotkeys, useHotkeysContext } from 'react-hotkeys-hook'
import { useState } from 'react'

function App() {
  return (
    <HotkeysProvider initiallyActiveScopes={['settings']}>
      <SettingsPanel />
    </HotkeysProvider>
  )
}

function SettingsPanel() {
  const [count, setCount] = useState(0)
  const { toggleScope } = useHotkeysContext()
  
  // This hotkey only works when 'settings' scope is active
  useHotkeys('ctrl+k', () => setCount(prevCount => prevCount + 1), { 
    scopes: ['settings'] 
  })

  return (
    <div>
      <p>Pressed {count} times</p>
      <button onClick={() => toggleScope('settings')}>
        Toggle Settings Scope
      </button>
    </div>
  )
}

Checking Pressed Keys

Check if a key is currently being held down:
import { isHotkeyPressed } from 'react-hotkeys-hook'

function MyComponent() {
  const handleClick = () => {
    if (isHotkeyPressed('shift')) {
      console.log('Shift-clicking!')
    } else {
      console.log('Normal click')
    }
  }
  
  return <button onClick={handleClick}>Click me</button>
}

Common Patterns

Command Palette

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

export const CommandPalette = () => {
  const [isOpen, setIsOpen] = useState(false)
  
  useHotkeys('mod+k', () => setIsOpen(true), { preventDefault: true })
  useHotkeys('esc', () => setIsOpen(false), { enabled: isOpen })
  
  if (!isOpen) return null
  
  return (
    <div className="command-palette">
      <input placeholder="Type a command..." autoFocus />
    </div>
  )
}

Keyboard Navigation

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

export const List = ({ items }) => {
  const [selectedIndex, setSelectedIndex] = useState(0)
  
  useHotkeys('up', () => setSelectedIndex(i => Math.max(0, i - 1)))
  useHotkeys('down', () => setSelectedIndex(i => Math.min(items.length - 1, i + 1)))
  useHotkeys('enter', () => console.log('Selected:', items[selectedIndex]))
  
  return (
    <ul>
      {items.map((item, index) => (
        <li 
          key={index}
          style={{ 
            background: index === selectedIndex ? 'lightblue' : 'white' 
          }}
        >
          {item}
        </li>
      ))}
    </ul>
  )
}

Form Submission

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

export const Form = () => {
  const handleSubmit = () => {
    console.log('Form submitted')
  }
  
  useHotkeys('ctrl+enter', handleSubmit, { 
    enableOnFormTags: ['textarea', 'input'] 
  })
  
  return (
    <form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
      <textarea placeholder="Press Ctrl+Enter to submit" />
      <button type="submit">Submit</button>
    </form>
  )
}

Next Steps

Now that you understand the basics, explore the full API documentation to learn about all available options and advanced features:

API Reference

Complete API documentation for all hooks and utilities

Core Concepts

Learn about scopes, sequences, and focus trapping

Build docs developers (and LLMs) love