Skip to main content

Default Behavior

By default, hotkeys are disabled when focus is inside form elements. This prevents accidental hotkey triggers while the user is typing.
import { useHotkeys } from 'react-hotkeys-hook'

function App() {
  // This won't trigger when typing in an input field
  useHotkeys('s', () => console.log('S pressed'))
  
  return <input type="text" placeholder="Type here" />
}
Typing “search” in the input won’t trigger the hotkey.

Enabling on All Form Tags

Set enableOnFormTags: true to enable hotkeys on all form elements:
import { useHotkeys } from 'react-hotkeys-hook'

function SearchBar() {
  useHotkeys('ctrl+k', handleSearch, {
    enableOnFormTags: true,
    preventDefault: true,
  })
  
  return <input type="text" placeholder="Search..." />
}

Enabling on Specific Tags

Enable hotkeys on only certain form elements:
import { useHotkeys } from 'react-hotkeys-hook'

function Form() {
  // Only active in input and textarea, not select
  useHotkeys('ctrl+enter', handleSubmit, {
    enableOnFormTags: ['input', 'textarea'],
    preventDefault: true,
  })
  
  return (
    <form>
      <input type="text" /> {/* ✓ Hotkey works here */}
      <textarea /> {/* ✓ Hotkey works here */}
      <select> {/* ✗ Hotkey doesn't work here */}
        <option>Option 1</option>
      </select>
    </form>
  )
}

Supported Form Tags

The following HTML tags and ARIA roles are recognized:

HTML Tags

  • input
  • textarea
  • select

ARIA Roles

  • searchbox
  • slider
  • spinbutton
  • menuitem
  • menuitemcheckbox
  • menuitemradio
  • option
  • radio
  • textbox
import { useHotkeys } from 'react-hotkeys-hook'

function AccessibleForm() {
  useHotkeys('ctrl+s', handleSave, {
    enableOnFormTags: [
      'input',
      'textarea',
      'searchbox', // ARIA role
      'textbox',   // ARIA role
    ],
  })
  
  return (
    <div>
      <input type="text" />
      <div role="searchbox" contentEditable />
      <div role="textbox" contentEditable />
    </div>
  )
}

ContentEditable Elements

For elements with contentEditable, use the enableOnContentEditable option:
import { useHotkeys } from 'react-hotkeys-hook'

function RichTextEditor() {
  useHotkeys('ctrl+b', makeBold, {
    enableOnContentEditable: true,
    preventDefault: true,
  })
  
  useHotkeys('ctrl+i', makeItalic, {
    enableOnContentEditable: true,
    preventDefault: true,
  })
  
  return (
    <div
      contentEditable
      style={{ border: '1px solid #ccc', padding: '1rem' }}
    >
      Type here...
    </div>
  )
}

Combining Both Options

You can enable hotkeys on both form tags and contentEditable:
import { useHotkeys } from 'react-hotkeys-hook'

function Editor() {
  useHotkeys('ctrl+s', handleSave, {
    enableOnFormTags: ['input', 'textarea'],
    enableOnContentEditable: true,
    preventDefault: true,
  })
  
  return (
    <div>
      <input type="text" /> {/* ✓ Works */}
      <div contentEditable> {/* ✓ Works */}
        Rich text content
      </div>
    </div>
  )
}

Common Use Cases

Submit Form on Ctrl+Enter

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

function CommentBox() {
  const [comment, setComment] = useState('')
  
  useHotkeys('ctrl+enter', () => handleSubmit(comment), {
    enableOnFormTags: ['textarea'],
    preventDefault: true,
  }, [comment])
  
  return (
    <form onSubmit={handleSubmit}>
      <textarea
        value={comment}
        onChange={(e) => setComment(e.target.value)}
        placeholder="Write a comment (Ctrl+Enter to submit)"
      />
      <button type="submit">Submit</button>
    </form>
  )
}

Search Bar Navigation

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

function SearchWithResults() {
  const [selectedIndex, setSelectedIndex] = useState(0)
  
  // Navigate results while typing in search
  useHotkeys('down', () => setSelectedIndex(i => i + 1), {
    enableOnFormTags: ['input'],
    preventDefault: true,
  })
  
  useHotkeys('up', () => setSelectedIndex(i => Math.max(0, i - 1)), {
    enableOnFormTags: ['input'],
    preventDefault: true,
  })
  
  useHotkeys('enter', () => selectResult(selectedIndex), {
    enableOnFormTags: ['input'],
    preventDefault: true,
  })
  
  return (
    <div>
      <input type="text" placeholder="Search..." />
      <ResultsList selectedIndex={selectedIndex} />
    </div>
  )
}

Markdown Editor Shortcuts

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

function MarkdownEditor() {
  const textareaRef = useRef<HTMLTextAreaElement>(null)
  
  const wrapSelection = (before: string, after: string) => {
    const textarea = textareaRef.current
    if (!textarea) return
    
    const start = textarea.selectionStart
    const end = textarea.selectionEnd
    const text = textarea.value
    const selection = text.substring(start, end)
    
    textarea.value = 
      text.substring(0, start) + 
      before + selection + after +
      text.substring(end)
  }
  
  useHotkeys('ctrl+b', () => wrapSelection('**', '**'), {
    enableOnFormTags: ['textarea'],
    preventDefault: true,
  })
  
  useHotkeys('ctrl+i', () => wrapSelection('*', '*'), {
    enableOnFormTags: ['textarea'],
    preventDefault: true,
  })
  
  useHotkeys('ctrl+k', () => wrapSelection('[', '](url)'), {
    enableOnFormTags: ['textarea'],
    preventDefault: true,
  })
  
  return (
    <textarea
      ref={textareaRef}
      placeholder="Write markdown..."
    />
  )
}

Autocomplete with Tab

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

function AutocompleteInput() {
  const [value, setValue] = useState('')
  const [suggestion, setSuggestion] = useState('')
  
  useHotkeys('tab', () => {
    if (suggestion) {
      setValue(suggestion)
      setSuggestion('')
    }
  }, {
    enableOnFormTags: ['input'],
    preventDefault: true,
  }, [suggestion])
  
  return (
    <div style={{ position: 'relative' }}>
      <input
        type="text"
        value={value}
        onChange={(e) => {
          setValue(e.target.value)
          setSuggestion(getSuggestion(e.target.value))
        }}
      />
      {suggestion && (
        <div style={{ position: 'absolute', color: '#999' }}>
          {suggestion}
        </div>
      )}
    </div>
  )
}

Excluding Specific Elements

If you enable on form tags but want to exclude specific elements, use the ignoreEventWhen option:
import { useHotkeys } from 'react-hotkeys-hook'

function Form() {
  useHotkeys('ctrl+s', handleSave, {
    enableOnFormTags: true,
    ignoreEventWhen: (event) => {
      const target = event.target as HTMLElement
      // Ignore if it's a password input
      return target.tagName === 'INPUT' && 
             (target as HTMLInputElement).type === 'password'
    },
  })
}

Shadow DOM Support

The library automatically handles custom elements and shadow DOM using composedPath():
import { useHotkeys } from 'react-hotkeys-hook'

function CustomEditor() {
  useHotkeys('ctrl+s', handleSave, {
    enableOnFormTags: ['input'],
  })
  
  return <custom-editor /> {/* Works even in shadow DOM */}
}
The default behavior (hotkeys disabled on form tags) is intentional to prevent conflicts with normal typing. Only enable on form tags when you have specific keyboard shortcuts for input interactions.
Be careful when enabling hotkeys on form tags - you might interfere with native browser shortcuts like Ctrl+A (select all) or Ctrl+C (copy). Use preventDefault carefully.
For rich text editors, enable both enableOnFormTags and enableOnContentEditable to support all editing surfaces.

Build docs developers (and LLMs) love