Skip to content
tactile

Tactile vs hotkeys-js

hotkeys-js is one of the most-used shortcut libraries on npm, and it deserves it: actively maintained, dependency-free, with a real scope system (setScope/getScope) and an input filter. Its two structural limits are the deprecated keyCode key model and scopes that are flat names rather than expressions.

hotkeys-js Tactile
Key model keyCode (deprecated) event.key + event.code, hybrid
Scopes one active named scope when expressions — compose freely
Conflicts last binding silently stacks detected + priority
Input filter global hotkeys.filter per-engine ignore
Sequences (g i)
Introspection key codes only full keymap with labels
Size ~2.6 kB ~5.5 kB

hotkeys-js has exactly one active scope at a time:

hotkeys('ctrl+b', 'editor', toggleBold);
hotkeys.setScope('editor');

Tactile’s scopes are just context keys, so conditions compose:

kb.add({
id: 'bold',
keys: 'ctrl+b',
when: "scope == 'editor' && !readOnly && !modalOpen",
handler: toggleBold,
});

With one active scope, “editor shortcuts, but not while a modal is open, but only when editable” forces scope-juggling in app code. With expressions it’s one line on the rule.

// hotkeys-js
hotkeys('ctrl+a,ctrl+b', 'editor', (event, handler) => {
if (handler.key === 'ctrl+a') selectAll();
else bold();
});
// Tactile — one rule per command (ids power help dialogs + collisions)
kb.add({ id: 'selectAll', keys: 'ctrl+a', when: "scope == 'editor'", handler: selectAll });
kb.add({ id: 'bold', keys: 'ctrl+b', when: "scope == 'editor'", handler: bold });
kb.context.set('scope', 'editor'); // replaces hotkeys.setScope('editor')