Version v2.1.0 Size 2.5 KB gzip Dependencies Zero dependencies
Why Keymap?
Browser keyboard handling needs modifier normalization, chord state, context policy, and listener ownership. Keymap keeps those concerns in one headless, zero-dependency handle.
ts
// Before
window.addEventListener('keydown', (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === 's') event.preventDefault();
});
// After
import { createKeymap } from '@vielzeug/keymap';
const map = createKeymap({ 'mod+s': () => console.log('save') });
const unmount = map.mount(document);
unmount();
map.dispose();| Feature | Raw addEventListener | Keymap |
|---|---|---|
| Bundle size | 0 B (built-in) | 2.5 KB |
| Zero dependencies | ||
| Chord sequences | ||
| Modifier aliases | cmd, win, option → canonical | |
| Context guards | Manual if in handler | Event-aware when(event) predicate |
| Chord ownership | Application-managed state | Per mounted target |
| Disposable | Manual removeEventListener | Terminal dispose() + [Symbol.dispose]() |
Use Keymap when you need chord sequences (g g, ctrl+k ctrl+s), modifier aliases, or context-scoped hotkeys that can be cleanly mounted and unmounted.
Consider raw addEventListener when you have a single, static, never-removed hotkey and don't need chords.
Installation
sh
pnpm add @vielzeug/keymapsh
npm install @vielzeug/keymapsh
yarn add @vielzeug/keymapQuick Start
Create, mount, then dispose one map owned by your UI scope.
ts
import { createKeymap } from '@vielzeug/keymap';
const map = createKeymap({
'mod+k mod+s': () => console.log('save'),
'mod+shift+p': () => console.log('open palette'),
'g g': () => window.scrollTo({ top: 0 }),
escape: () => console.log('close panel'),
});
const unmount = map.mount(document);
unmount();
map.dispose();Features
createKeymap()— Create a keymap from a bindings record; mount to anyEventTarget- Chord sequences —
"g g","ctrl+k ctrl+s"with configurable timeout (default 1 s) - Modifier aliases —
cmd/command/win→meta;opt/option→alt;mod→ platform-aware BindingOptions— per-binding{ handler, when?, trigger? }object syntaxmodKeyoption — explicit platform override for SSR and cross-platform testsformatShortcut()— platform-aware display (⇧⌘Pon Mac,Ctrl+Shift+Pelsewhere)parseShortcut()/parseStep()/matchStep()— exposed for building custom matchers or testingcanonicalizeShortcut()— convert any shortcut alias to a stable key for conflict detectiondetectModKey()— platform modifier detection ('meta'on Mac,'ctrl'elsewhere)listBindings()— snapshot all active bindings (shortcut and trigger) for palette UIsfindShortcutConflicts()— detect prefix/duplicate conflicts before binding a user-customized shortcut- Disposable —
dispose()+[Symbol.dispose]forusingdeclarations