Skip to main content
Key sequences allow you to create multi-step keyboard shortcuts where keys must be pressed in a specific order, similar to Vim’s command mode or Emacs key bindings.

What Are Key Sequences?

A key sequence is a series of keys that must be pressed in order to trigger an action. For example, the famous “Konami Code” (up, up, down, down, left, right, left, right, b, a) is a key sequence.
Key sequences use the > character by default to separate keys in the sequence.

Basic Key Sequence

Create a sequence by separating keys with the > character:
import { useHotkeys } from 'react-hotkeys-hook';

function App() {
  useHotkeys('y>e>e>t', () => {
    console.log('You typed "yeet"!');
  });

  return <div>Try typing: y-e-e-t</div>;
}
Each key must be pressed in order within the timeout period (default: 1000ms).

Sequence Timeout

By default, you have 1 second (1000ms) between each key press. Customize this with the sequenceTimeoutMs option:
// Give users 2 seconds between keys
useHotkeys('g>g', () => {
  console.log('Go to top (Vim style)');
}, { sequenceTimeoutMs: 2000 });

// Faster sequences - 500ms timeout
useHotkeys('k>o', () => {
  console.log('Quick combo');
}, { sequenceTimeoutMs: 500 });
If the timeout expires or an incorrect key is pressed, the sequence resets and you must start over.

Practical Examples

Vim-Style Navigation

function VimNavigation() {
  // Go to top of page
  useHotkeys('g>g', () => {
    window.scrollTo({ top: 0, behavior: 'smooth' });
  });

  // Go to bottom of page
  useHotkeys('shift+g', () => {
    window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
  });

  // Delete line
  useHotkeys('d>d', () => {
    console.log('Delete current line');
  });

  return <div>Use Vim-style navigation!</div>;
}

Command Palette Sequence

function CommandPalette() {
  const [isOpen, setIsOpen] = useState(false);

  // Open command palette with 'g' then 'o'
  useHotkeys('g>o', () => {
    setIsOpen(true);
  });

  // Quick actions
  useHotkeys('g>h', () => window.location.href = '/');
  useHotkeys('g>s', () => window.location.href = '/settings');
  useHotkeys('g>p', () => window.location.href = '/profile');

  return (
    <div>
      <p>Try: g→h (home), g→s (settings), g→p (profile)</p>
      {isOpen && <div>Command palette opened!</div>}
    </div>
  );
}

Secret Code Easter Egg

function KonamiCode() {
  const [activated, setActivated] = useState(false);

  useHotkeys('up>up>down>down>left>right>left>right>b>a', () => {
    setActivated(true);
    console.log('🎮 Konami Code activated!');
  }, { sequenceTimeoutMs: 1000 });

  return (
    <div>
      {activated && (
        <div className="easter-egg">
          🎉 You found the secret!
        </div>
      )}
    </div>
  );
}

Combining Sequences with Other Hotkeys

You can mix sequences with regular hotkeys in the same component:
function Editor() {
  // Regular hotkey
  useHotkeys('ctrl+s', () => {
    console.log('Quick save');
  });

  // Sequence
  useHotkeys('g>g', () => {
    console.log('Go to top');
  });

  // Multiple hotkeys including a sequence
  useHotkeys(['d>d', 'y', 'meta+a'], (event, hotkeysEvent) => {
    if (hotkeysEvent.isSequence) {
      console.log('Sequence triggered:', hotkeysEvent.hotkey);
    } else {
      console.log('Regular hotkey triggered:', hotkeysEvent.hotkey);
    }
  });

  return <div>Editor with mixed shortcuts</div>;
}
You can check if a hotkey is a sequence by inspecting hotkeysEvent.isSequence in your callback.

Custom Sequence Separator

Change the default > separator using the sequenceSplitKey option:
// Use '-' instead of '>'
useHotkeys('y-e-e-t', () => {
  console.log('Custom separator');
}, { sequenceSplitKey: '-' });

// Use ',' as separator
useHotkeys('a,b,c', () => {
  console.log('ABC sequence');
}, { 
  delimiter: '|',        // Separator for multiple hotkeys
  sequenceSplitKey: ',' // Separator within a sequence
});
Don’t mix combinations (+) and sequences (>) in the same hotkey string. This is not supported and will log a warning.
// ❌ Invalid - mixing + and >
useHotkeys('ctrl+a>b', callback); // Will show warning and not work

// ✅ Valid - combination only
useHotkeys('ctrl+shift+a', callback);

// ✅ Valid - sequence only
useHotkeys('a>b>c', callback);

Sequence Behavior Details

1

User presses first key

The sequence detection starts. A timer begins counting down (default 1000ms).
2

User presses subsequent keys

Each correct key press resets the timer. If an incorrect key is pressed, the entire sequence resets.
3

Sequence completes

When all keys are pressed in order, the callback fires and the sequence resets.
4

Timeout or wrong key

If the timer expires or a wrong key is pressed, the sequence resets and the user must start over.

Modifiers in Sequences

Currently, modifier keys (ctrl, shift, alt, meta) are not fully supported within sequences. Modifiers are filtered out during sequence detection.
// ✅ This works - plain keys
useHotkeys('g>g', callback);

// ⚠️ This may not work as expected - modifiers in sequence
useHotkeys('ctrl>a>b', callback); // Ctrl will be ignored

// ✅ Use modifiers outside sequences
useHotkeys('shift+g>g', callback); // Not a sequence, treated as combination
For now, stick to plain keys (letters, numbers, arrows, etc.) in your sequences.

Using Produced Keys in Sequences

Enable the useKey option to listen to the actual character produced rather than the physical key code:
// Listen to produced characters (useful for symbols)
useHotkeys('%>!', () => {
  console.log('Symbol sequence triggered');
}, { useKey: true });

// Without useKey (default), listens to key codes
useHotkeys('5>1', () => {
  console.log('Number sequence by key code');
}, { useKey: false });
useKey: true is helpful when working with symbols or special characters that require modifier keys to type.

Resetting Sequences

Sequences automatically reset in these situations:
  1. Timeout expires - No key pressed within sequenceTimeoutMs
  2. Wrong key pressed - User presses a key that doesn’t match the next expected key
  3. Sequence completes - All keys pressed successfully and callback executed
  4. Component unmounts - The hook cleans up and resets
function SequenceExample() {
  useHotkeys('a>b>c', () => {
    console.log('Sequence complete!');
  }, { sequenceTimeoutMs: 1000 });

  // If user types: a → b → (waits 1+ seconds) → c
  // Sequence resets after the wait, must start with 'a' again

  // If user types: a → b → x
  // Sequence resets immediately, must start with 'a' again

  return <div>Type: a-b-c</div>;
}

Complex Sequence Patterns

GitHub-Style Quick Navigation

function GitHubStyleNav() {
  useHotkeys('g>i', () => window.location.href = '/issues');
  useHotkeys('g>p', () => window.location.href = '/pulls');
  useHotkeys('g>c', () => window.location.href = '/code');
  useHotkeys('g>w', () => window.location.href = '/wiki');
  
  return (
    <nav>
      <p>Press 'g' then:</p>
      <ul>
        <li>'i' for Issues</li>
        <li>'p' for Pull Requests</li>
        <li>'c' for Code</li>
        <li>'w' for Wiki</li>
      </ul>
    </nav>
  );
}

Multi-Action Sequences

function MultiActionSequence() {
  const [mode, setMode] = useState('normal');

  // Enter command mode
  useHotkeys(':', () => {
    setMode('command');
  });

  // Sequences only work in command mode
  useHotkeys('w>q', () => {
    console.log('Save and quit');
  }, { 
    enabled: mode === 'command',
    sequenceTimeoutMs: 2000 
  });

  useHotkeys('q>!', () => {
    console.log('Quit without saving');
  }, { 
    enabled: mode === 'command',
    sequenceTimeoutMs: 2000 
  });

  // Exit command mode
  useHotkeys('esc', () => {
    setMode('normal');
  });

  return (
    <div>
      <p>Mode: {mode}</p>
      <p>Press ':' to enter command mode, then 'wq' or 'q!'</p>
    </div>
  );
}

Best Practices

  • Keep sequences short (2-4 keys) for better user experience
  • Use intuitive key combinations that are easy to remember
  • Provide visual feedback showing what keys the user has pressed in a sequence
  • Set appropriate timeouts based on your use case (longer for complex sequences)
  • Document your sequences in your UI so users can discover them
  • Test sequences thoroughly as they can be tricky to get right

Build docs developers (and LLMs) love