Tactile vs Mousetrap: a modern Mousetrap alternative
Mousetrap defined the string-binding style every library since has
copied ('ctrl+shift+k', 'g i' sequences, the Konami code demo). It’s tiny, dependency-free, and
battle-tested — and it’s showing its age: it matches on the deprecated keyCode, has no built-in
scopes, and has seen little maintenance in years.
At a glance
Section titled “At a glance”| Mousetrap | Tactile | |
|---|---|---|
| Key model | event.which/keyCode (deprecated) |
event.key + event.code, hybrid |
| International layouts | breaks (QWERTZ/AZERTY/Dvorak) | layout-aware by design |
| Scopes / context | none (mousetrap-global-bind plugin for inputs) |
when expressions |
| Conflicts | silent | detected + priority |
| Sequences | ✅ 'g i' |
✅ 'g i' |
| Inputs/textarea | skipped, plugin to override | skipped, per-rule enableInFormFields |
| Size | ~2.2 kB | ~5.5 kB |
Where Mousetrap still wins
Section titled “Where Mousetrap still wins”Size and ubiquity. If you have three global shortcuts on a US-layout-only internal tool, Mousetrap (or tinykeys) is plenty.
Migration
Section titled “Migration”The binding syntax is nearly identical — most strings port unchanged:
// MousetrapMousetrap.bind('mod+k', openPalette);Mousetrap.bind('g i', goToInbox);Mousetrap.unbind('mod+k');// Tactileimport { createKeybindingEngine } from '@tactile-js/core';const kb = createKeybindingEngine();
const off = kb.add({ id: 'palette', keys: 'mod+k', handler: openPalette });kb.add({ id: 'inbox', keys: 'g i', handler: goToInbox });off(); // unbindDifferences to watch:
- Tactile rules need a stable
id(it powers collisions, introspection, and help dialogs). Mousetrap.bindGlobal(the plugin for shortcuts inside inputs) becomesenableInFormFields: trueon the rule — no plugin, and sequences stay safely off while typing.- Letters match the physical keycap by default (what
keyCodeusers expect on US layouts, but correct everywhere else too).