Skip to main content

Overview

You can control when hotkeys are active using the enabled option. This is useful for disabling shortcuts in certain UI states or based on user permissions.

Using the Enabled Option

Boolean Value

The simplest way to disable a hotkey is with a boolean:
import { useHotkeys } from 'react-hotkeys-hook'
import { useState } from 'react'

function EditableContent() {
  const [isEditing, setIsEditing] = useState(false)
  
  // Only active when NOT editing
  useHotkeys('e', () => setIsEditing(true), {
    enabled: !isEditing,
  })
  
  // Only active when editing
  useHotkeys('escape', () => setIsEditing(false), {
    enabled: isEditing,
  })
  
  return (
    <div>
      {isEditing ? (
        <input autoFocus onBlur={() => setIsEditing(false)} />
      ) : (
        <p>Press 'e' to edit</p>
      )}
    </div>
  )
}

State-Based Enabling

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

function Document() {
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
  const [isOnline, setIsOnline] = useState(true)
  
  // Only enable save when there are changes AND online
  useHotkeys('ctrl+s', handleSave, {
    enabled: hasUnsavedChanges && isOnline,
    preventDefault: true,
  })
  
  return <div>Content</div>
}

Dynamic Enable/Disable with Functions

For more complex logic, use a function that returns a boolean:
import { useHotkeys } from 'react-hotkeys-hook'

function AdvancedEditor() {
  useHotkeys('ctrl+b', handleBold, {
    enabled: (keyboardEvent, hotkeysEvent) => {
      // Check if we're in a text input
      const target = keyboardEvent.target as HTMLElement
      const isInput = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA'
      
      // Only enable if in an input AND user has permission
      return isInput && hasEditPermission()
    },
  })
}

Runtime Validation

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

function ConditionalHotkeys() {
  useHotkeys('ctrl+delete', handleDelete, {
    enabled: (keyboardEvent) => {
      // Don't allow delete if readonly mode
      if (isReadOnly) return false
      
      // Don't allow delete if nothing is selected
      if (!hasSelection()) return false
      
      // Require confirmation for destructive action
      return window.confirm('Delete selected items?')
    },
    preventDefault: true,
  })
}

Based on User Permissions

import { useHotkeys } from 'react-hotkeys-hook'
import { useAuth } from './auth'

function AdminPanel() {
  const { user } = useAuth()
  
  // Admin-only hotkey
  useHotkeys('ctrl+shift+a', openAdminPanel, {
    enabled: user?.role === 'admin',
  })
  
  // Pro user features
  useHotkeys('ctrl+shift+p', openProFeatures, {
    enabled: user?.plan === 'pro',
  })
}

Feature Flags

import { useHotkeys } from 'react-hotkeys-hook'
import { useFeatureFlag } from './features'

function BetaFeatures() {
  const aiAssistEnabled = useFeatureFlag('ai-assist')
  
  useHotkeys('ctrl+space', openAIAssist, {
    enabled: aiAssistEnabled,
    description: 'Open AI assistant',
  })
}

Loading States

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

function DataTable() {
  const [isLoading, setIsLoading] = useState(false)
  
  // Disable navigation while loading
  useHotkeys('j', moveDown, { enabled: !isLoading })
  useHotkeys('k', moveUp, { enabled: !isLoading })
  useHotkeys('r', refresh, { enabled: !isLoading })
  
  return <div>Table content</div>
}

Modal/Dialog States

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

function App() {
  const [isModalOpen, setIsModalOpen] = useState(false)
  
  // Disable global shortcuts when modal is open
  useHotkeys('ctrl+k', openCommandPalette, {
    enabled: !isModalOpen,
  })
  
  // Modal-specific shortcuts
  useHotkeys('escape', () => setIsModalOpen(false), {
    enabled: isModalOpen,
  })
  
  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>Open Modal</button>
      {isModalOpen && <Modal />}
    </div>
  )
}

With Dependencies

When using state in the enabled function, include it in dependencies:
import { useHotkeys } from 'react-hotkeys-hook'
import { useState } from 'react'

function Editor() {
  const [mode, setMode] = useState<'view' | 'edit'>('view')
  const [hasChanges, setHasChanges] = useState(false)
  
  useHotkeys(
    'ctrl+s',
    handleSave,
    {
      enabled: mode === 'edit' && hasChanges,
      preventDefault: true,
    },
    [mode, hasChanges] // Include state in dependencies
  )
}

Temporarily Disable All Hotkeys

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

function useGlobalHotkeyToggle() {
  const [hotkeysEnabled, setHotkeysEnabled] = useState(true)
  
  return { hotkeysEnabled, setHotkeysEnabled }
}

function App() {
  const { hotkeysEnabled, setHotkeysEnabled } = useGlobalHotkeyToggle()
  
  // All hotkeys respect the global toggle
  useHotkeys('ctrl+s', handleSave, { enabled: hotkeysEnabled })
  useHotkeys('ctrl+k', handleCommand, { enabled: hotkeysEnabled })
  useHotkeys('ctrl+p', handlePrint, { enabled: hotkeysEnabled })
  
  return (
    <div>
      <label>
        <input
          type="checkbox"
          checked={hotkeysEnabled}
          onChange={(e) => setHotkeysEnabled(e.target.checked)}
        />
        Enable keyboard shortcuts
      </label>
    </div>
  )
}

Performance Tip

When disabling hotkeys based on state, the hook still listens to keyboard events. For better performance with many hotkeys, consider using scopes instead:
import { useHotkeys, HotkeysProvider, useHotkeysContext } from 'react-hotkeys-hook'

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

function Editor() {
  const { enableScope, disableScope } = useHotkeysContext()
  
  // Instead of enabled: isViewMode
  useHotkeys('j', moveDown, { scopes: 'view' })
  useHotkeys('k', moveUp, { scopes: 'view' })
  
  // Instead of enabled: isEditMode
  useHotkeys('ctrl+b', makeBold, { scopes: 'edit' })
  useHotkeys('ctrl+i', makeItalic, { scopes: 'edit' })
  
  const switchToEditMode = () => {
    disableScope('view')
    enableScope('edit')
  }
}
When enabled is false, the hook still listens to keyboard events but doesn’t trigger the callback. This is different from scopes, where inactive scopes don’t process events at all.
For better performance with many hotkeys that toggle together, use scopes instead of the enabled option.

Build docs developers (and LLMs) love