UNPKG

@tanstack/hotkeys

Version:

Type-safe, framework-agnostic keyboard hotkey management for the browser

63 lines (53 loc) • 3.97 kB
--- name: app-hotkeys description: >- Use when an app built with TanStack Hotkeys needs a keyboard shortcut cheat sheet or help dialog, a command palette whose commands may have shortcuts, a settings screen where users rebind, unbind, or reset shortcuts, recording the shortcut a user presses, conflict checks between shortcuts, or saving custom shortcuts to storage. metadata: purpose: >- Declare every shortcut once with defineAppHotkeys, then build cheat sheets, rebinding, conflict checks, and persistence from those declarations instead of a parallel shortcut registry. type: core library: '@tanstack/hotkeys' library_version: '0.16.0' sources: - src/app-hotkeys.ts - src/hotkey-recorder.ts - src/conflicts.ts - TanStack/hotkeys:docs/framework/react/guides/app-hotkeys.md - TanStack/hotkeys:docs/framework/react/guides/cheat-sheet-ui-helpers.md - TanStack/hotkeys:docs/framework/react/guides/custom-hotkey-recording.md - 'TanStack/hotkeys:packages/react-hotkeys/tests/skills/shortcut-settings/**' requires: - hotkeys-core --- # App hotkeys This skill builds on `hotkeys-core`. The adapter skill's app hotkeys reference shows the wiring in your framework. App hotkeys are the library's shortcut catalog. Use them instead of a separate shortcut config, context, or hand-written cheat sheet. One module declares every shortcut. The root provider holds the user's custom hotkeys. Components register the declared app hotkeys with their callbacks. Plain hotkeys with `meta` are enough when a list only needs what is mounted right now. ```ts import { defineAppHotkeys } from '@tanstack/hotkeys' export const appHotkeys = defineAppHotkeys({ 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save' }, scope: 'document' }, 'comment.post': { hotkey: 'Mod+S', meta: { name: 'Post comment' }, scope: 'comments', }, 'theme.toggle': { hotkey: null, meta: { name: 'Toggle theme' } }, }) ``` Augment `Register` in the adapter module you import from, such as `declare module '@tanstack/react-hotkeys' { interface Register { appHotkeys: typeof appHotkeys } }`, to type ids. Lit, Alpine, and Ember have no provider: call `provideAppHotkeys(appHotkeys, options)` once at startup. ## Pitfalls - **Register the app hotkey, not its value.** Pass `appHotkeys['doc.save']`. Passing `.hotkey` registers the declared default, so the user's rebinding never applies, and the list reports the shortcut as `'inactive'`. - **The provider reads `customHotkeys` once.** When saved shortcuts load asynchronously, render the provider after they arrive, or key it by user. Change shortcuts afterwards through the adapter's custom hotkeys API, not by updating the prop. - **Save what `onCustomHotkeysChange` receives.** It holds only the shortcuts the user changed, so users still get new defaults. Saving every effective hotkey freezes today's defaults into their data. - **Ids are a storage contract.** Saved data is keyed by id, and unknown ids are dropped with a warning, so renaming an id discards users' customizations. - **`setCustomHotkey` returns conflicts and changes nothing when there are any.** Check the result before reporting success, or reject the key press earlier by calling `findConflicts` in the recorder's `validate`. - **Declare scopes on app hotkeys that share keys.** An app hotkey without a scope, or in `'global'`, conflicts with every other app hotkey on the same keys, so rebinding is rejected. - **Conflict checks treat physical and logical bindings as different.** A recorded `Mod+[KeyP]` doesn't conflict with a declared `Mod+P`, although Command+P runs both. Declaring rebindable defaults in the recorder's form, such as `Mod+[KeyP]`, makes them comparable. - **A cheat sheet dialog usually gets an exclusive scope.** Register the hotkey that opened it inside the dialog too, so the same keys close it. When building the "press a new shortcut" control, read [recording](references/recording.md).