Skip to content
keymap logoKeymapApp Infrastructure
Target-local keyboard shortcut manager with chords, event-aware guards, modifier aliases, and terminal disposal.
Version
v2.1.0
Size
2.5 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22SSRDeno
canonicalizeShortcutcreateKeymapdetectModKeyfindShortcutConflictsformatShortcut View all 10 exports

Why Keymap?

Browser keyboard handling needs modifier normalization, chord state, context policy, and listener ownership. Keymap keeps those concerns in one headless, zero-dependency handle.

ts
// Before
window.addEventListener('keydown', (event) => {
  if ((event.ctrlKey || event.metaKey) && event.key === 's') event.preventDefault();
});

// After
import { createKeymap } from '@vielzeug/keymap';

const map = createKeymap({ 'mod+s': () => console.log('save') });
const unmount = map.mount(document);

unmount();
map.dispose();
FeatureRaw addEventListenerKeymap
Bundle size0 B (built-in)2.5 KB
Zero dependencies
Chord sequences
Modifier aliasescmd, win, option → canonical
Context guardsManual if in handlerEvent-aware when(event) predicate
Chord ownershipApplication-managed statePer mounted target
DisposableManual removeEventListenerTerminal dispose() + [Symbol.dispose]()

Use Keymap when you need chord sequences (g g, ctrl+k ctrl+s), modifier aliases, or context-scoped hotkeys that can be cleanly mounted and unmounted.

Consider raw addEventListener when you have a single, static, never-removed hotkey and don't need chords.

Installation

sh
pnpm add @vielzeug/keymap
sh
npm install @vielzeug/keymap
sh
yarn add @vielzeug/keymap

Quick Start

Create, mount, then dispose one map owned by your UI scope.

ts
import { createKeymap } from '@vielzeug/keymap';

const map = createKeymap({
  'mod+k mod+s': () => console.log('save'),
  'mod+shift+p': () => console.log('open palette'),
  'g g': () => window.scrollTo({ top: 0 }),
  escape: () => console.log('close panel'),
});

const unmount = map.mount(document);

unmount();
map.dispose();

Features

  • createKeymap() — Create a keymap from a bindings record; mount to any EventTarget
  • Chord sequences — "g g", "ctrl+k ctrl+s" with configurable timeout (default 1 s)
  • Modifier aliases — cmd/command/winmeta; opt/optionalt; mod → platform-aware
  • BindingOptions — per-binding { handler, when?, trigger? } object syntax
  • modKey option — explicit platform override for SSR and cross-platform tests
  • formatShortcut() — platform-aware display (⇧⌘P on Mac, Ctrl+Shift+P elsewhere)
  • parseShortcut() / parseStep() / matchStep() — exposed for building custom matchers or testing
  • canonicalizeShortcut() — convert any shortcut alias to a stable key for conflict detection
  • detectModKey() — platform modifier detection ('meta' on Mac, 'ctrl' elsewhere)
  • listBindings() — snapshot all active bindings (shortcut and trigger) for palette UIs
  • findShortcutConflicts() — detect prefix/duplicate conflicts before binding a user-customized shortcut
  • Disposable — dispose() + [Symbol.dispose] for using declarations

Documentation

See Also

  • Herald — Typed event bus; pair with Keymap by publishing shortcut events to a bus instead of calling handlers directly
  • Refineore-command-palette uses Keymap internally; register your own shortcuts alongside it
  • Ore — Attach a keymap inside a define() setup function for component-scoped shortcuts