The fastest, most flexible keyboard binding library for web apps.
import { keymash, ctrl, shift, press } from 'keymash';
const km = keymash();
// Type-safe, autocompletes, catches typos at compile time
km.bind(ctrl + press.s, () => save());
km.bind(ctrl + shift + press.p, () => commandPalette());
// Want Ctrl+K or Ctrl+O to open search? Use |
km.bind(ctrl + (press.k | press.o), () => openSearch());npm install keymashOther keyboard libraries make you write "$mod+([0-9])" and hope you got their special syntax right. Keymash uses TypeScript operators that autocomplete.
- International Keyboards — AltGr on European layouts, IME for CJK input, dead key compose sequences. Doesn't break the edge cases other libraries ignore.
- Physical Key Codes — WASD game controls that work on AZERTY, QWERTZ, Dvorak. Bind by position, not character.
- Strong Types — TypeScript operators that autocomplete. Typos caught at compile time, not runtime.
- Tiny and Fast — ~2.86 KB gzipped. O(1) chord lookup using bigint bitmasks. Zero dependencies.
- Modal UIs Made Simple — Multiple instances with independent state. Toggle between vim modes, command palettes, focus traps.
- Conflict Detection — Dev mode warns about duplicate bindings and browser shortcuts you shouldn't override.
| Module | Description | Size |
|---|---|---|
keymash |
Full library with introspection and dev warnings | ~2.86 KB |
keymash/react |
React hooks for declarative keyboard binding | ~3.79 KB |
import { keymash, ctrl, press } from 'keymash';
const km = keymash({
bindings: [
{ combo: ctrl + press.s, handler: () => save(), label: 'Save' },
{ combo: ctrl + press.z, handler: () => undo(), label: 'Undo' },
]
});const editor = document.getElementById('editor');
const editorKm = keymash({
scope: editor,
bindings: [
{ combo: ctrl + press.b, handler: () => bold() },
]
});
// Only active when focus is inside #editorimport { useKeymash, ctrl, press } from 'keymash/react';
function Editor() {
const { isActive, triggered } = useKeymash({
bindings: [
{ combo: ctrl + press.s, handler: () => save(), label: 'Save' },
],
});
return <div>Last action: {triggered?.label}</div>;
}Full documentation, interactive demo, and API reference at zetlen.github.io/keymash.
MIT