combokeys

A dependency-free JavaScript library for binding keyboard shortcuts, combinations, and key sequences to any DOM element.

Library
npm
v3.0.1
673stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
39/100Needs Attention
Development Activity0
Maintenance20
Community56
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
62/100Good
Architecture60
Code Quality65
Innovation58
Learning Curve65

Combokeys is a small, dependency-free JavaScript library for handling keyboard shortcuts in the browser. Forked from the popular Mousetrap library, it refactors the original as a CommonJS module and turns it into a constructor you instantiate against a specific DOM element, rather than a singleton that silently attaches itself to document.

That one change makes it possible to run multiple independent instances scoped to different elements — a modal, an editor pane, a sidebar — each with its own bindings, without one instance’s shortcuts leaking into another’s. It supports single keys, key combinations (command+shift+k), and Gmail-style key sequences (g i), across keypress, keydown, and keyup events, and ships a small set of official plugins for dictionary-style binding, global (input-field-safe) bindings, pausing, and sequence recording.

What You Get

  • A Combokeys constructor you instantiate per DOM element, so multiple independent shortcut scopes can coexist on one page
  • Support for single keys, +-joined combinations, and space-separated Gmail-style key sequences (e.g. g i, up up down down)
  • Explicit control over which event type triggers a binding (keypress, keydown, or keyup)
  • A trigger() method to programmatically fire any bound combination, plus unbind, reset, and detach for cleanup
  • Official plugins for bind-dictionary (object-style multi-binding), global-bind (bindings that fire even inside input fields), pause/unpause, and sequence recording

Common Use Cases

  • Adding power-user keyboard shortcuts to a web app’s editor or command palette
  • Building Gmail-style sequence shortcuts (e.g. g i for ‘go to inbox’)
  • Scoping shortcuts to a specific widget or modal without affecting the rest of the page
  • Recording a user-defined shortcut for later playback via the record plugin

Under The Hood

Architecture Combokeys structures its prototype methods as one file per method under Combokeys/prototype/ (bind.js, bindSingle.js, bindSequence.js, bindMultiple.js, unbind.js, trigger.js, handleKey.js, getKeyInfo.js, getMatches.js, getReverseMap.js, resetSequences.js, resetSequenceTimer.js, fireCallback.js, stopCallback.js, addEvents.js, detach.js), each required and assigned onto module.exports.prototype from Combokeys/index.js, which also owns the constructor that seeds per-instance state (callbacks, directMap, sequenceLevels, resetTimer) and pushes the instance onto a shared Combokeys.instances array when storeInstancesGlobally is set. bind delegates through bindMultiple to bindSingle, which detects multi-key sequences and hands them to bindSequence for reprocessing one key at a time — a clean, traceable call chain. The separation of concerns is real (each file does one job), but state isn’t fully instance-local: the reverse key-map and the instances registry live as statics on the constructor itself, and the official plugins work by monkey-patching methods like Combokeys.bind at the prototype level rather than through a dedicated extension point, so a plugin ordering mistake would ripple across every instance sharing that prototype.

Tech Stack The library is vanilla CommonJS JavaScript with no runtime dependencies at all — package.json declares only devDependencies: standard for linting, browserify to produce a UMD dist/combokeys.js bundle, mocha plus proclaim and sinon for the browser test suite, es5-shim/es5-sham to backfill ES5 behavior for legacy browsers, phantomjs as a headless test browser, and zuul to drive that suite across real and virtual browsers in CI. The npm test script chains lint, cross-browser unit tests via zuul, and the browserify build into one pipeline, and .travis.yml wires that into Travis CI. There’s no bundler configuration beyond the browserify invocation itself and no TypeScript — the whole surface is plain ES5-flavored JavaScript targeting old and new browsers alike.

Code Quality Test coverage is genuinely extensive for a library this size: test/ mirrors the public API one file per concern (bind.js, unbind.js, detach.js, initialization.js) plus a full plugins/ subtree exercising each official plugin, all written against mocha with proclaim assertions and sinon for spies and stubs. Linting is enforced via standard (a zero-config ESLint preset) with an explicit dist/** ignore. Source files favor small, single-purpose functions and consistent JSDoc-style comment blocks documenting parameter types and return shapes, though the codebase predates real JSDoc/TypeScript tooling so those comments can’t be statically checked. There’s no type system, so functions like getKeyInfo and getMatches rely entirely on the test suite and code review to keep their informally-documented contracts intact.

API Design The public surface is deliberately small — bind, unbind, trigger, stopCallback, detach, reset — and getting started requires only instantiating new Combokeys(element) and calling .bind(), mirroring the ergonomics of the Mousetrap library it was forked from. That familiarity is also its ceiling: the fork’s headline improvement, per-element instances instead of a document-wide singleton, is a meaningful developer-experience win for apps with multiple independent shortcut scopes, but the binding syntax, sequence format, and plugin model are carried over largely unchanged from the original rather than rethought, so there’s little here a Mousetrap user wouldn’t already recognize.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search