Skip to main content
Scopes allow you to control which hotkeys are active at any given time. This is useful for modal dialogs, different views, or any scenario where you want certain hotkeys to only work in specific contexts.

Understanding Scopes

Scopes work by:
  1. Wrapping your app with HotkeysProvider to manage active scopes
  2. Assigning hotkeys to specific scopes using the scopes option
  3. Enabling/disabling scopes dynamically to control which hotkeys are active

Setup with HotkeysProvider

First, wrap your application with the HotkeysProvider:
import { HotkeysProvider } from 'react-hotkeys-hook';

function App() {
  return (
    <HotkeysProvider initiallyActiveScopes={['*']}>
      <YourApp />
    </HotkeysProvider>
  );
}
The default scope is '*' (wildcard), which means all hotkeys without explicit scopes are active by default.

Assigning Hotkeys to Scopes

Use the scopes option to assign hotkeys to specific scopes:
import { useHotkeys } from 'react-hotkeys-hook';

function Editor() {
  // Only active when 'editor' scope is enabled
  useHotkeys('ctrl+s', () => {
    console.log('Save in editor');
  }, { scopes: 'editor' });

  // Only active when 'viewer' scope is enabled
  useHotkeys('space', () => {
    console.log('Play/pause in viewer');
  }, { scopes: 'viewer' });

  return <div>...</div>;
}

Multiple Scopes

A hotkey can belong to multiple scopes:
useHotkeys('esc', () => {
  console.log('Close dialog');
}, { scopes: ['dialog', 'modal', 'overlay'] });
The hotkey will be active if any of the specified scopes are enabled.

Managing Active Scopes

Use the useHotkeysContext hook to control which scopes are active:
import { useHotkeys, useHotkeysContext } from 'react-hotkeys-hook';

function NavigationPanel() {
  const { activeScopes, enableScope, disableScope, toggleScope } = useHotkeysContext();

  return (
    <div>
      <button onClick={() => enableScope('editor')}>
        Enable Editor
      </button>
      <button onClick={() => disableScope('editor')}>
        Disable Editor
      </button>
      <button onClick={() => toggleScope('viewer')}>
        Toggle Viewer
      </button>
      <p>Active scopes: {activeScopes.join(', ')}</p>
    </div>
  );
}

Available Scope Functions

  • enableScope(scope) - Activates a scope, making all hotkeys in that scope active
  • disableScope(scope) - Deactivates a scope, disabling all hotkeys in that scope
  • toggleScope(scope) - Toggles a scope between active and inactive
  • activeScopes - Array of currently active scope names

Practical Example: Modal Dialog

Here’s a complete example showing how to use scopes with a modal:
import { useHotkeys, useHotkeysContext, HotkeysProvider } from 'react-hotkeys-hook';
import { useState } from 'react';

function App() {
  return (
    <HotkeysProvider initiallyActiveScopes={['app']}>
      <MainApp />
    </HotkeysProvider>
  );
}

function MainApp() {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const { enableScope, disableScope } = useHotkeysContext();

  // App-level hotkey
  useHotkeys('ctrl+k', () => {
    setIsModalOpen(true);
    enableScope('modal');
    disableScope('app');
  }, { scopes: 'app' });

  const closeModal = () => {
    setIsModalOpen(false);
    disableScope('modal');
    enableScope('app');
  };

  return (
    <div>
      <h1>Main App</h1>
      <p>Press Ctrl+K to open modal</p>
      {isModalOpen && <Modal onClose={closeModal} />}
    </div>
  );
}

function Modal({ onClose }) {
  // Modal-specific hotkeys
  useHotkeys('esc', onClose, { scopes: 'modal' });
  
  useHotkeys('enter', () => {
    console.log('Submit modal');
    onClose();
  }, { scopes: 'modal' });

  return (
    <div className="modal">
      <h2>Modal Dialog</h2>
      <p>Press ESC to close or Enter to submit</p>
    </div>
  );
}

Scope Priority

1

Wildcard Scope Behavior

When the wildcard scope '*' is active, all hotkeys without explicit scopes work. Once you enable a specific scope, the wildcard is automatically removed.
2

Multiple Active Scopes

Multiple scopes can be active simultaneously. Hotkeys will trigger if they belong to any active scope.
3

Scope Takes Precedence

The scopes option takes precedence over the enabled option. Even if enabled: true, a hotkey won’t trigger if its scope is not active.
function Example() {
  // This will NOT work if 'editor' scope is not active,
  // even though enabled is true
  useHotkeys('ctrl+s', () => {
    console.log('Save');
  }, { 
    scopes: 'editor',
    enabled: true  // Scope takes precedence
  });
}

Without HotkeysProvider

If you try to use scopes without wrapping your app in HotkeysProvider, the hotkeys will not work. The provider is required for scope management.
// ❌ This won't work - no provider
function Component() {
  useHotkeys('a', () => console.log('A'), { scopes: 'test' });
  return <div>...</div>;
}

// ✅ This works - has provider
function App() {
  return (
    <HotkeysProvider>
      <Component />
    </HotkeysProvider>
  );
}

Advanced: View-Based Scopes

Manage hotkeys across different views in your application:
import { useHotkeys, useHotkeysContext } from 'react-hotkeys-hook';
import { useEffect } from 'react';

function EditorView() {
  const { enableScope, disableScope } = useHotkeysContext();

  // Activate editor scope when component mounts
  useEffect(() => {
    enableScope('editor');
    return () => disableScope('editor');
  }, []);

  // Editor-specific hotkeys
  useHotkeys('ctrl+b', () => console.log('Bold'), { scopes: 'editor' });
  useHotkeys('ctrl+i', () => console.log('Italic'), { scopes: 'editor' });
  useHotkeys('ctrl+u', () => console.log('Underline'), { scopes: 'editor' });

  return <div>Editor View</div>;
}

function ViewerView() {
  const { enableScope, disableScope } = useHotkeysContext();

  useEffect(() => {
    enableScope('viewer');
    return () => disableScope('viewer');
  }, []);

  // Viewer-specific hotkeys
  useHotkeys('space', () => console.log('Play/Pause'), { scopes: 'viewer' });
  useHotkeys('left', () => console.log('Rewind'), { scopes: 'viewer' });
  useHotkeys('right', () => console.log('Forward'), { scopes: 'viewer' });

  return <div>Viewer View</div>;
}

Debugging Active Scopes

You can display the currently active scopes to help debug your scope configuration:
import { useHotkeysContext } from 'react-hotkeys-hook';

function ScopeDebugger() {
  const { activeScopes } = useHotkeysContext();

  return (
    <div style={{ position: 'fixed', bottom: 10, right: 10, background: '#333', color: '#fff', padding: '10px' }}>
      <strong>Active Scopes:</strong>
      <ul>
        {activeScopes.map(scope => (
          <li key={scope}>{scope}</li>
        ))}
      </ul>
    </div>
  );
}

Best Practices

  • Use descriptive scope names like 'editor', 'modal', 'settings' rather than generic names
  • Clean up scopes when components unmount to prevent memory leaks
  • Use the wildcard scope '*' for global hotkeys that should always work
  • Consider using useEffect to automatically manage scope lifecycle with component mounting/unmounting

Build docs developers (and LLMs) love