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
User presses first key
The sequence detection starts. A timer begins counting down (default 1000ms).
User presses subsequent keys
Each correct key press resets the timer. If an incorrect key is pressed, the entire sequence resets.
Sequence completes
When all keys are pressed in order, the callback fires and the sequence resets.
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:
- Timeout expires - No key pressed within
sequenceTimeoutMs
- Wrong key pressed - User presses a key that doesn’t match the next expected key
- Sequence completes - All keys pressed successfully and callback executed
- 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