Tactile: a JavaScript keyboard shortcut library
Tactile is a keyboard-shortcut engine that takes context and keyboard layouts seriously.
Most shortcut libraries are built on the deprecated KeyboardEvent.keyCode, treat scopes as an
afterthought, and leave you to discover binding conflicts at runtime. Tactile starts from a different
place: a small framework-agnostic core modeled on VS Code’s keybinding system structured rules, a
when-expression context system, and explicit collision resolution with thin adapters on top.
What makes it different
Section titled “What makes it different”- Layout-aware matching. Bindings match against
event.keyandevent.codedepending on what the key is — never the deprecatedkeyCode.⌘Zlands on the physical Z key across layouts;?matches the character you’d expect. See Match modes and International keyboards. whenexpressions, not flags. Gate any rule with a boolean expression over context keys (scope == 'editor' && !readOnly). Named scopes are just sugar over context keys — one concept to learn, not two.- Collisions are first-class.
getCollisions()tells you when two rules fight over the same keystroke; at runtime, ties resolve by priority then registration order, the same model VS Code uses. - Introspection built in.
getKeymap()returns every binding with platform-formatted labels — help dialogs that can’t drift from what’s registered. - A real core/adapter split. The engine talks to a
KeyEventSourceinterface, not the DOM directly — fully unit-tested in Node and reusable by any framework adapter.
Packages
Section titled “Packages”| Package | What it is |
|---|---|
@tactile-js/core |
The framework-agnostic engine. API reference. |
@tactile-js/react |
React hooks — overview. |
Documentation map
Section titled “Documentation map”| I want to… | Start here |
|---|---|
| Install and write my first shortcut | Installation → Quick start |
Understand code vs key / Dvorak |
Match modes → International keyboards |
| Gate shortcuts by UI state | When expressions |
| Build a command palette or help dialog | Recipes |
| See every API option | Core API → Engine options |
| Try it in the browser | Playground |
How do I add keyboard shortcuts to a JavaScript app?
Section titled “How do I add keyboard shortcuts to a JavaScript app?”Install @tactile-js/core, create an engine with createKeybindingEngine(), and register rules
with kb.add({ id, keys, handler }). React apps can use the useShortcut
hook from @tactile-js/react instead. See the Quick start.
How do I support ⌘ on Mac and Ctrl on Windows with one binding?
Section titled “How do I support ⌘ on Mac and Ctrl on Windows with one binding?”Use the mod alias: keys: 'mod+k' resolves to ⌘K on macOS and Ctrl+K on Windows and Linux — one
binding string for every platform. See Key syntax.
Why do keyboard shortcuts break on international keyboard layouts?
Section titled “Why do keyboard shortcuts break on international keyboard layouts?”Most libraries rely on the deprecated keyCode, which maps inconsistently across layouts like
QWERTZ, AZERTY, and Dvorak. Tactile matches on event.key and event.code with a hybrid
strategy — ⌘Z stays on the physical Z key while symbols like ? match the character. See
Match modes.
How do I stop shortcuts from firing while the user types in an input?
Section titled “How do I stop shortcuts from firing while the user types in an input?”By default Tactile ignores key events from input, textarea, select, and contenteditable
elements. To let a specific shortcut through anyway — a command palette’s mod+k, say — set
enableInFormFields: true on that one rule, or replace the engine-wide
ignore predicate for broader policies.
What happens when two shortcuts use the same key combination?
Section titled “What happens when two shortcuts use the same key combination?”Tactile detects collisions at registration time via getCollisions() and resolves them at runtime
by priority, then registration order — the same precedence model VS Code uses. See
Collision detection.
Ready? Head to Installation.