npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-keyhub

v1.1.6

Published

A lightweight, scalable keyboard shortcut manager for React applications with TypeScript support

Downloads

99

Readme

React KeyHub

A lightweight, scalable keyboard shortcut manager for React applications with TypeScript support.

npm version npm downloads license CI

Demo

Features

  • 🔑 Central Configuration: Define all keyboard shortcuts in one place
  • 🔄 Type Safety: Full TypeScript support for shortcut definitions and hooks
  • 🎯 Optimized Performance: Single event listener with efficient lookup
  • 🧩 Modular API: Subscribe to shortcuts from any component
  • 📋 Built-in Shortcut Sheet: Display all registered shortcuts in a user-friendly format
  • 🔌 Zero Dependencies: No external dependencies (aside from React)
  • 🔄 Dynamic Updates: Enable, disable, or modify shortcuts at runtime
  • 🌐 Context Awareness: Define shortcuts that only work in specific contexts
  • 🔢 Sequence Support: Create shortcuts that require a sequence of key presses
  • 🎨 Theming Support: Light, dark, and auto themes for the shortcut sheet
  • 📱 Responsive Layouts: Modal and sidebar layouts for the shortcut sheet
  • 💡 Type Suggestions: Enhanced hooks with autocomplete for registered shortcuts

Installation

npm install react-keyhub
# or
yarn add react-keyhub

Quick Start

import React, { useState } from 'react';
import { 
  KeyHubProvider, 
  useShortcut,
  useKeyboardShortcut,
  ShortcutSheet,
  defaultShortcuts 
} from 'react-keyhub';

// Define your shortcuts (or use the default ones)
const myShortcuts = {
  ...defaultShortcuts,
  customAction: {
    keyCombo: 'ctrl+k',
    name: 'Custom Action',
    description: 'Perform a custom action',
    scope: 'global',
    priority: 100,
    status: 'enabled',
    group: 'Custom',
    type: 'regular'
  },
};

// Your app component
function App() {
  const [isShortcutSheetOpen, setShortcutSheetOpen] = useState(false);
  
  // Use the enhanced hook with type suggestions
  const isSaveRegistered = useShortcut('save', (e) => {
    console.log('Save triggered!');
    // Your save logic here
  });
  
  // You can also use the backward compatibility hooks
  useKeyboardShortcut('customAction', (e) => {
    console.log('Custom action triggered!');
    // Your custom action logic here
  });
  
  // Toggle the shortcut sheet
  useShortcut('showShortcuts', () => {
    setShortcutSheetOpen(prev => !prev);
  });
  
  return (
    <div>
      <h1>My App</h1>
      <p>Press Ctrl+/ to see all shortcuts</p>
      <p>Save shortcut registered: {isSaveRegistered ? 'Yes' : 'No'}</p>
      
      {/* Shortcut Sheet */}
      <ShortcutSheet 
        isOpen={isShortcutSheetOpen} 
        onClose={() => setShortcutSheetOpen(false)} 
      />
    </div>
  );
}

// Wrap your app with the provider
function Root() {
  return (
    <KeyHubProvider shortcuts={myShortcuts}>
      <App />
    </KeyHubProvider>
  );
}

export default Root;

API Reference

KeyHubProvider

The provider component that makes shortcuts available throughout your application.

<KeyHubProvider 
  shortcuts={myShortcuts} 
  options={{ 
    preventDefault: true,
    stopPropagation: true,
    debounceTime: 0,
    sequenceTimeout: 1000,
    ignoreInputFields: true,
    ignoreModifierOnlyEvents: true
  }}
>
  {children}
</KeyHubProvider>

Props

  • shortcuts: A record of shortcut configurations
  • options (optional):
    • preventDefault: Whether to prevent the default browser behavior (default: true)
    • stopPropagation: Whether to stop event propagation (default: true)
    • target: The element to attach the event listener to (default: document)
    • debounceTime: Debounce time in milliseconds (default: 0)
    • sequenceTimeout: Timeout for sequence shortcuts in milliseconds (default: 1000)
    • ignoreInputFields: Whether to ignore keyboard events from input fields (default: true)
    • ignoreModifierOnlyEvents: Whether to ignore keyboard events that only contain modifier keys (default: true)

useShortcut

A hook to subscribe to a keyboard shortcut with type suggestions.

// The shortcutId will have type suggestions for all registered shortcuts
// TypeScript will show an error for non-existent shortcuts
const isSaveRegistered = useShortcut('save', (e) => {
  console.log('Save shortcut triggered!');
  // Your save logic here
});

// The hook returns a boolean indicating if the shortcut is registered
console.log('Is save shortcut registered?', isSaveRegistered);

Parameters

  • shortcutId: The ID of the shortcut to subscribe to (with type suggestions based on provider shortcuts)
  • callback: The callback to execute when the shortcut is triggered

Return Value

  • boolean: Indicates if the shortcut is registered

Type Safety

The hook uses the actual shortcuts provided to the KeyHubProvider for type checking:

import { 
  ShortcutScope, 
  ShortcutStatus, 
  ShortcutType, 
  ShortcutSettings 
} from 'react-keyhub';

// Define custom shortcuts
const myShortcuts = {
  ...defaultShortcuts,
  customAction: {
    keyCombo: 'ctrl+shift+c',
    name: 'Custom Action',
    description: 'A custom action shortcut',
    scope: ShortcutScope.GLOBAL,
    priority: 100,
    status: ShortcutStatus.ENABLED,
    group: 'Custom',
    type: ShortcutType.REGULAR
  }
} as ShortcutSettings;

// In your component
function MyComponent() {
  // This will work fine
  useShortcut('customAction', () => {});
  
  // This will cause a TypeScript error
  useShortcut('nonExistentShortcut', () => {});
}

Error Handling

The hook checks if the shortcut is registered and provides a warning if it's not:

// This will log a warning if 'nonExistentShortcut' is not registered
useShortcut('nonExistentShortcut', (e) => {});
// Warning: Shortcut "nonExistentShortcut" is not registered. Available shortcuts: save, saveAs, print, ...

useKeyboardShortcut and useKey

For backward compatibility, useKeyboardShortcut and useKey are also available as aliases for useShortcut:

// These are all equivalent
useShortcut('save', callback);
useKeyboardShortcut('save', callback);
useKey('save', callback);

AvailableShortcuts

A type that provides suggestions for all registered shortcuts based on what's provided to the KeyHubProvider:

import { AvailableShortcuts } from 'react-keyhub';

// This will have type suggestions for all registered shortcuts
// based on what's provided to the KeyHubProvider
const shortcutId: AvailableShortcuts = 'save';

// If you've added a custom shortcut, it will be included in the suggestions
const customShortcutId: AvailableShortcuts = 'customAction'; // Works if customAction is registered

getRegisteredShortcuts

A function to get all registered shortcuts from the current provider:

import { getRegisteredShortcuts } from 'react-keyhub';

function MyComponent() {
  // This will return the shortcuts from the current provider
  const shortcuts = getRegisteredShortcuts();
  
  return (
    <div>
      <h2>Registered Shortcuts</h2>
      <ul>
        {Object.entries(shortcuts).map(([id, config]) => (
          <li key={id}>
            {id}: {config.name} - {config.type === 'regular' ? config.keyCombo : config.sequence}
          </li>
        ))}
      </ul>
    </div>
  );
}

useShortcutSheet

A hook to get all registered shortcuts.

const shortcuts = useShortcutSheet();

useShortcutStatus

A hook to enable or disable a shortcut.

useShortcutStatus('save', true); // Enable the "save" shortcut
useShortcutStatus('save', false); // Disable the "save" shortcut

useShortcutUpdate

A hook to update a shortcut configuration.

useShortcutUpdate('save', {
  keyCombo: 'ctrl+shift+s',
  priority: 200,
});

useShortcutRegister

A hook to register a new shortcut dynamically.

useShortcutRegister('myDynamicShortcut', {
  keyCombo: 'ctrl+d',
  name: 'Dynamic Shortcut',
  description: 'A dynamically registered shortcut',
  scope: 'global',
  priority: 100,
  status: 'enabled',
  group: 'Dynamic',
  type: 'regular'
});

useShortcutContext

A hook to set the active context.

useShortcutContext('editor'); // Set the active context to "editor"
useShortcutContext(null); // Clear the active context

useShortcutPause

A hook to pause and resume the event bus.

useShortcutPause(true); // Pause all shortcuts
useShortcutPause(false); // Resume all shortcuts

useShortcutGroups

A hook to get all shortcut groups.

const groups = useShortcutGroups(); // Returns an array of group names

useShortcutsByGroup

A hook to get shortcuts by group.

const fileShortcuts = useShortcutsByGroup('File'); // Returns all shortcuts in the "File" group

useKeyHub

A hook to access the KeyHub event bus directly.

const eventBus = useKeyHub();

// Now you can use the event bus methods
eventBus.on('save', callback);
eventBus.off(subscriptionId);
eventBus.enableShortcut('save');
eventBus.disableShortcut('save');
eventBus.updateShortcut('save', { priority: 200 });
eventBus.registerShortcut('myShortcut', { ... });
eventBus.unregisterShortcut('myShortcut');
eventBus.setContext('editor');
eventBus.getContext();
eventBus.pause();
eventBus.resume();
eventBus.getShortcuts();
eventBus.getShortcutsByGroup('File');
eventBus.getShortcutGroups();

ShortcutSheet

A component to display all registered shortcuts.

<ShortcutSheet 
  isOpen={isOpen} 
  onClose={handleClose} 
  theme="light" // 'light', 'dark', or 'auto'
  layout="modal" // 'modal' or 'sidebar'
  filter={{ 
    scope: 'global',
    search: 'save',
    group: 'File',
    context: 'editor'
  }}
/>

Props

  • isOpen: Whether to show the shortcut sheet
  • onClose: Callback to close the shortcut sheet
  • theme (optional): Theme for the shortcut sheet ('light', 'dark', or 'auto')
  • layout (optional): Layout for the shortcut sheet ('modal' or 'sidebar')
  • filter (optional): Filter for the shortcuts to display
    • scope (optional): Filter by scope ('global' or 'local')
    • search (optional): Filter by search term
    • group (optional): Filter by group
    • context (optional): Filter by context
  • className (optional): Custom class name for the shortcut sheet

ShortcutSheetStyles

A string of CSS styles for the ShortcutSheet component.

import { ShortcutSheetStyles } from 'react-keyhub';

// Add the styles to your app
const App = () => (
  <>
    <style>{ShortcutSheetStyles}</style>
    {/* Your app content */}
  </>
);

Shortcut Configuration

Each shortcut is defined with the following properties:

Regular Shortcut

{
  keyCombo: 'ctrl+s',
  name: 'Save',
  description: 'Save the current document',
  scope: 'global',
  priority: 100,
  status: 'enabled',
  group: 'File',
  context: 'editor', // Optional
  type: 'regular',
  action: (e) => { /* Optional default action */ }
}

Sequence Shortcut

{
  sequence: 'g c', // 'g' followed by 'c'
  name: 'Git Commands',
  description: 'Show git commands menu',
  scope: 'global',
  priority: 100,
  status: 'enabled',
  group: 'Git',
  context: 'editor', // Optional
  type: 'sequence',
  action: (e) => { /* Optional default action */ }
}

Properties

  • keyCombo (for regular shortcuts): The key combination (e.g., 'ctrl+s', 'ctrl+shift+n')
  • sequence (for sequence shortcuts): The sequence of key combinations (e.g., 'g c' for "g" followed by "c")
  • name: A human-readable name for the shortcut
  • description: A detailed description of what the shortcut does
  • scope: Either 'global' or 'local'
  • priority: The priority of the shortcut (higher numbers take precedence)
  • status: Either 'enabled' or 'disabled'
  • group: A group for the shortcut (used for organizing in the shortcut sheet)
  • context (optional): A context for the shortcut (only active when the context matches)
  • type: Either 'regular' or 'sequence'
  • action (optional): A default action to execute when the shortcut is triggered

Default Shortcuts

React KeyHub comes with a set of default shortcuts organized by groups:

File Operations

  • save: Ctrl+S
  • saveAs: Ctrl+Shift+S
  • print: Ctrl+P
  • newWindow: Ctrl+Shift+N

Edit Operations

  • find: Ctrl+F
  • replace: Ctrl+H
  • undo: Ctrl+Z
  • redo: Ctrl+Y
  • cut: Ctrl+X
  • copy: Ctrl+C
  • paste: Ctrl+V
  • selectAll: Ctrl+A

Navigation

  • goToLine: Ctrl+G
  • goToFile: Ctrl+P (lower priority than print)

Help

  • help: F1
  • showShortcuts: Ctrl+/

Git (Sequence Shortcuts)

  • gitCommands: g c (press "g" then "c")
  • gitStatus: g s (press "g" then "s")

Vim Navigation (Context-Specific)

  • vimUp: k (only active in "vim" context)
  • vimDown: j (only active in "vim" context)
  • vimLeft: h (only active in "vim" context)
  • vimRight: l (only active in "vim" context)

You can use these as a starting point and override or extend them as needed.

Advanced Usage

Context-Aware Shortcuts

// Define shortcuts with contexts
const myShortcuts = {
  ...defaultShortcuts,
  editorSave: {
    keyCombo: 'ctrl+s',
    name: 'Save',
    description: 'Save the current document',
    scope: 'global',
    priority: 100,
    status: 'enabled',
    group: 'File',
    context: 'editor', // Only active in "editor" context
    type: 'regular'
  },
  terminalClear: {
    keyCombo: 'ctrl+l',
    name: 'Clear Terminal',
    description: 'Clear the terminal',
    scope: 'global',
    priority: 100,
    status: 'enabled',
    group: 'Terminal',
    context: 'terminal', // Only active in "terminal" context
    type: 'regular'
  }
};

// In your component, set the active context
function EditorComponent() {
  // Set the active context to "editor"
  useShortcutContext('editor');
  
  // ...
}

function TerminalComponent() {
  // Set the active context to "terminal"
  useShortcutContext('terminal');
  
  // ...
}

Sequence Shortcuts

// Define sequence shortcuts
const myShortcuts = {
  ...defaultShortcuts,
  gitCommit: {
    sequence: 'g c', // Press "g" then "c"
    name: 'Git Commit',
    description: 'Open git commit dialog',
    scope: 'global',
    priority: 100,
    status: 'enabled',
    group: 'Git',
    type: 'sequence'
  }
};

// Subscribe to the sequence shortcut
function MyComponent() {
  useShortcut('gitCommit', () => {
    console.log('Git commit dialog opened');
  });
  
  // ...
}

Dynamic Shortcut Registration

function MyComponent() {
  // Register a dynamic shortcut
  useShortcutRegister('dynamicShortcut', {
    keyCombo: 'ctrl+d',
    name: 'Dynamic Shortcut',
    description: 'A dynamically registered shortcut',
    scope: 'global',
    priority: 100,
    status: 'enabled',
    group: 'Dynamic',
    type: 'regular',
    action: () => {
      console.log('Dynamic shortcut triggered!');
    }
  });
  
  // The shortcut will be automatically unregistered when the component unmounts
  
  // ...
}

Pausing and Resuming Shortcuts

function MyComponent() {
  const [isPaused, setIsPaused] = useState(false);
  
  // Pause or resume all shortcuts
  useShortcutPause(isPaused);
  
  return (
    <div>
      <button onClick={() => setIsPaused(!isPaused)}>
        {isPaused ? 'Resume Shortcuts' : 'Pause Shortcuts'}
      </button>
    </div>
  );
}

Themed Shortcut Sheet

function MyComponent() {
  const [isOpen, setIsOpen] = useState(false);
  const [theme, setTheme] = useState<'light' | 'dark' | 'auto'>('auto');
  
  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Show Shortcuts</button>
      
      <select value={theme} onChange={(e) => setTheme(e.target.value as any)}>
        <option value="light">Light</option>
        <option value="dark">Dark</option>
        <option value="auto">Auto (System)</option>
      </select>
      
      <ShortcutSheet 
        isOpen={isOpen} 
        onClose={() => setIsOpen(false)} 
        theme={theme}
      />
    </div>
  );
}

Browser Support

React KeyHub works in all modern browsers that support React.

License

MIT