+```
+
+An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered.
+
+## Customize and persist hotkeys
+
+TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change:
+
+```svelte
+
+
+
+ localStorage.setItem('hotkeys', JSON.stringify(custom))}
+>
+
+
+```
+
+`setHotkeysContext` accepts the same `appHotkeys`, `customHotkeys`, and `onCustomHotkeysChange` options.
+
+- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them.
+- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning.
+- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile.
+
+Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others.
+
+### Change hotkeys
+
+`getCustomHotkeys` reads and changes the user's custom hotkeys:
+
+```svelte
+
+
+
+```
+
+| Member | Description |
+| --- | --- |
+| `current` | The hotkeys that differ from the defaults |
+| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. |
+| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` |
+| `resetCustomHotkey(id)` | Restores one default |
+| `resetCustomHotkeys()` | Restores every default |
+
+Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered.
+
+Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it.
diff --git a/docs/framework/svelte/guides/formatting-display.md b/docs/framework/svelte/guides/formatting-display.md
index f7e4a8fa..c050a311 100644
--- a/docs/framework/svelte/guides/formatting-display.md
+++ b/docs/framework/svelte/guides/formatting-display.md
@@ -3,7 +3,7 @@ title: Formatting & Display Guide
id: formatting-display
---
-Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string.
+Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string.
## Format a binding
@@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true })
With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`.
-Sequences are arrays of bindings. Format their steps individually:
+## Format sequences
-```ts
-import type { HotkeySequence } from '@tanstack/svelte-hotkeys'
+Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default:
-const sequence: HotkeySequence = ['Mod+[KeyK]', 'C']
-const label = sequence.map((step) => formatForDisplay(step)).join(' → ')
+```ts
+formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C'
+formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G'
+formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true })
+// [['⌘', 'K'], ['C']] — one array of keycaps per step
```
-`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels.
+With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps.
## Choose symbols and separators
@@ -63,7 +65,7 @@ formatForDisplay('Control++', {
}) // 'Ctrl · +'
```
-An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`.
+An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps.
## Supply layout labels
@@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', {
The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only.
+## Compare hotkeys
+
+`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord.
+
## Parse and store bindings
`parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity:
@@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S'
Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field.
-Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts).
+Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts).
Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay).
diff --git a/docs/framework/svelte/guides/hotkey-recording.md b/docs/framework/svelte/guides/hotkey-recording.md
index 57608648..70bcf8c4 100644
--- a/docs/framework/svelte/guides/hotkey-recording.md
+++ b/docs/framework/svelte/guides/hotkey-recording.md
@@ -3,114 +3,131 @@ title: Hotkey Recording Guide
id: hotkey-recording
---
-TanStack Hotkeys provides the `createHotkeyRecorder` function for building shortcut customization UIs in Svelte.
+`createHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too.
Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead.
-TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release.
-
-## Reactive options
-
-Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update.
+TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release.
## Basic usage
```svelte
-
-
- {#if recorder.isRecording}
-
- {/if}
-
+
```
-## Return value
+## Recorder state and actions
+
+| Member | Description |
+| --- | --- |
+| `isRecording` | Whether the recorder is currently listening for key presses |
+| `steps` | Chords captured so far in a multi-step recording |
+| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded |
+| `startRecording()` | Start listening for key presses |
+| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` |
+| `cancelRecording()` | Stop listening and call `onCancel` |
+| `commitRecording()` | Commit the current `steps` now (multi-step recordings) |
-- `isRecording`: whether recording is active
-- `recordedHotkey`: the most recently recorded hotkey
-- `startRecording()`: start listening for key presses
-- `stopRecording()`: stop listening and reset recorder state without calling `onRecord`
-- `cancelRecording()`: stop listening and discard the in-progress recording
+State members are reactive getters. Read them from the recorder object instead of destructuring them once.
## Options
### `recordBy`
-Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. There is no code-to-letter conversion. Existing authored strings such as `Mod+S` remain logical bindings.
+Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys.
-Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. Recording events, repeats, and their releases do not trigger registered hotkeys or sequences.
+Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift.
-```ts
-createHotkeyRecorder({
- recordBy: 'code', // default; choose 'key' for logical characters
- onRecord: (hotkey) => {},
- onCancel: () => {},
- onClear: () => {},
-})
-```
+### `onRecord`
-Options can also be reactive:
+Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise.
-```svelte
-
-```
+### `onClear`
+
+Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default.
### `ignoreInputs`
-The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements.
+Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements.
+
+Options can also be a getter, such as `createHotkeyRecorder(() => ({ maxSteps: 3, recordBy: mode, onRecord }))`, so recording policy can follow `$state`.
+
+### Default options
+
+Set defaults for every recorder with `HotkeysProvider` (or `setHotkeysContext` in a root component's script). Per-recorder options override them.
+
+```svelte
+
+
+
+```
+
+## Recording sequences
+
+Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step:
```ts
-createHotkeyRecorder({
- ignoreInputs: false, // record even from inside inputs
- onRecord: (hotkey) => console.log(hotkey),
+const recorder = createHotkeyRecorder({
+ maxSteps: 3,
+ onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]']
})
+
+// Live preview while recording
+const label = $derived(
+ recorder.isRecording
+ ? formatForDisplay(recorder.steps) || 'Press chords, then Enter…'
+ : formatForDisplay(recorder.recordedHotkey ?? []),
+)
```
+A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit.
+
+| Option | Description |
+| --- | --- |
+| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. |
+| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. |
+| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. |
+
+`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord.
+
## Recording behavior
-- Modifier-only presses do not complete a recording.
-- Modifier plus key combinations record the full shortcut.
-- Escape cancels recording.
-- Backspace and Delete clear the shortcut.
-- Recorded values are normalized to portable `Mod` format.
+| Key | Behavior |
+| --- | --- |
+| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step |
+| Modifier + key (e.g., Ctrl+S) | Records the full combination |
+| Single key (e.g., Escape, F1) | Records the single key |
+| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps |
+| Escape | Cancels the recording |
+| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` |
+
+Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows.
## Validation and conflicts
-Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`:
+The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`:
```ts
const options = {
detectConflicts: {
- // Use registration IDs to exclude the binding being edited:
+ // Use registration IDs to exclude the hotkey being edited:
excludeIds: [registrationId],
target: document,
eventType: 'keydown',
@@ -124,8 +141,62 @@ const options = {
}
```
-`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity.
+`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace.
+
+`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute.
+
+For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position.
+
+## Building a shortcut settings UI
+
+For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place.
+
+For a single component, ordinary state is enough:
+
+```svelte
+
+
+{#each Object.entries(shortcuts) as [action, hotkey] (action)}
+
+{/each}
+```
-`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch.
+## Under the hood
-For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position.
+`createHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state through reactive getters, and destroys it when the component unmounts.
diff --git a/docs/framework/svelte/guides/hotkeys.md b/docs/framework/svelte/guides/hotkeys.md
index 66fa0f1b..241ded19 100644
--- a/docs/framework/svelte/guides/hotkeys.md
+++ b/docs/framework/svelte/guides/hotkeys.md
@@ -5,6 +5,18 @@ id: hotkeys
Use `createHotkey` for global shortcuts and `createHotkeyAttachment` for element-scoped shortcuts. The common global case stays simple, and scoped behavior feels native to Svelte 5.
+## Chords and sequences
+
+A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`.
+
+```ts
+createHotkey('Mod+S', () => save()) // chord
+createHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // sequence
+createHotkey('Escape', () => close(), { requireReset: true }) // chord-only option
+```
+
+Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them.
+
## Logical keys and physical positions
Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position:
@@ -265,7 +277,7 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro
## Introspecting registrations
-Use the `getHotkeyRegistrations` function to get a live view of all hotkey and sequence registrations. This is useful for building shortcut palettes, help dialogs, or devtools.
+Use the `getHotkeyRegistrations` function to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs.
```svelte
-
-
Keyboard Shortcuts
-
- {#each registrations.hotkeys as reg (reg.hotkey)}
-
```
-The returned object contains a `hotkeys` array with registration objects including the hotkey string, options (including `meta`), and enabled state, and a `sequences` array containing sequence registrations with the same structure.
+The returned object's reactive `hotkeys` getter holds one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel.
+
+Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys).
## The hotkey manager
diff --git a/docs/framework/svelte/guides/key-state-tracking.md b/docs/framework/svelte/guides/key-state-tracking.md
index af575606..f12149be 100644
--- a/docs/framework/svelte/guides/key-state-tracking.md
+++ b/docs/framework/svelte/guides/key-state-tracking.md
@@ -104,6 +104,6 @@ tracker.isKeyHeld('Shift')
const hint = getHotkeyHint(() => binding) // reactive getter: hint.visible
```
-For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection.
+For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound).
Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`.
diff --git a/docs/framework/svelte/guides/sequence-recording.md b/docs/framework/svelte/guides/sequence-recording.md
deleted file mode 100644
index 14eab8c5..00000000
--- a/docs/framework/svelte/guides/sequence-recording.md
+++ /dev/null
@@ -1,58 +0,0 @@
----
-title: Sequence Recording Guide
-id: sequence-recording
----
-
-Use `createHotkeySequenceRecorder` to capture a series of shortcut chords. By default, each step records its physical code: pressing G twice produces `['[KeyG]', '[KeyG]']`. Set `recordBy: 'key'` to follow logical characters instead. Pass the resulting array directly to sequence registration and format each step for display.
-
-TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release.
-
-## Reactive options
-
-Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update.
-
-## Record and display a sequence
-
-This example uses a Save button so plain Enter can be recorded as a step:
-
-```svelte
-
-
-
-
-{#if recorder.isRecording}
-
-
-{/if}
-```
-
-## State and controls
-
-The recorder exposes `isRecording`, `steps`, and `recordedSequence` as reactive getters. `steps` contains the current attempt; `recordedSequence` contains the last committed result. `startRecording()` begins a new session. `commitRecording()` saves a nonempty attempt, while `cancelRecording()` discards it and calls `onCancel`. `stopRecording()` resets recorder state without calling `onRecord` or `onCancel`.
-
-## Options and keyboard behavior
-
-- `recordBy`: `'code'` by default; `'key'` records produced logical characters.
-- `commitKeys`: `'enter'` by default; plain Enter commits a nonempty sequence. `'none'` lets Enter become a step and requires manual or idle commit.
-- `commitOnEnter: false`: also permits Enter as a step when `commitKeys` is `'enter'`.
-- `idleTimeoutMs`: optionally commits after inactivity following a completed step. No timer runs before the first step.
-- `ignoreInputs`: true by default, including shadow-root inputs. Set false to record from editable fields. Escape still cancels from an input.
-
-Escape cancels. Unmodified Backspace/Delete removes the last step; when already empty it stops and calls only `onClear`. Modifier-only presses, automatic repeats, and IME composition do not append steps. Recorded events and their releases do not trigger application shortcuts.
-
-Set provider defaults through `HotkeysProvider` with `defaultOptions.hotkeySequenceRecorder`.
-
-## Validation and conflicts
-
-`validate(sequence, { events, parsedSequence })` runs at commit. Return true to accept, or false/a message to reject. `detectConflicts` checks live bindings and sequence prefixes, including physical/logical overlap established by the recorded events. `onReject` receives feedback; rejected steps remain editable with Backspace.
-
-The shared options and exclusions are described in the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). The application owns reset, persistence, and any binding being edited; clearing never calls `onRecord([])`.
diff --git a/docs/framework/svelte/guides/sequences.md b/docs/framework/svelte/guides/sequences.md
index 33c344f5..e5b993a1 100644
--- a/docs/framework/svelte/guides/sequences.md
+++ b/docs/framework/svelte/guides/sequences.md
@@ -3,162 +3,143 @@ title: Sequences Guide
id: sequences
---
-TanStack Hotkeys supports multi-key sequences in Svelte, where keys are pressed one after another rather than simultaneously.
+A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `createHotkey` and `createHotkeys`, list them with `getHotkeyRegistrations`, record them with `createHotkeyRecorder`, and format them with `formatForDisplay`.
-Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`.
+Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`.
-## Reactive options
+## Basic usage
-Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same.
-
-## Global sequences
+Pass an array of chords wherever you pass a hotkey:
```svelte
```
+The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`.
+
## Many sequences at once
-Use `createHotkeySequences` to register several global sequences in one place (including from a reactive getter). For multiple sequences on a focused element, use `createHotkeySequencesAttachment` the same way you would use `createHotkeySequenceAttachment`.
+`createHotkeys` registers chords and sequences together, including dynamic lists:
-```svelte
-
+createHotkeys([
+ { hotkey: ['G', 'G'], callback: () => scrollToTop() },
+ { hotkey: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } },
+ { hotkey: 'Mod+S', callback: () => save() },
+])
```
-## Scoped sequences
+## Sequence options
-Use `createHotkeySequenceAttachment` when a sequence should only be active while a specific element owns focus.
+Sequences accept the same options as chords, except `requireReset`, plus `timeout`.
-```svelte
-
+The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second).
-
- Focus here, then press g then g
-
+```ts
+// Fast sequence - user must type quickly
+createHotkey(['D', 'D'], () => deleteLine(), { timeout: 500 })
+
+// Slow sequence - user has more time between keys
+createHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 })
```
-## Matching steps
+`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider` (or `setHotkeysContext` in a root component's script):
+
+```svelte
+
+
+
+```
-Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches.
+### `enabled` and `meta`
-## Sequence options
+These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys).
```ts
-createHotkeySequence(['G', 'G'], callback, {
- timeout: 1000,
- enabled: true,
-})
+createHotkey(['G', 'G'], () => scrollToTop(), () => ({
+ enabled: isVimMode,
+ meta: { name: 'Go to top', description: 'Scroll to the top of the page' },
+}))
```
-### Reactive `enabled`
+## Sequences with modifiers
-When disabled, the sequence stays registered (visible in devtools); only execution is suppressed.
+Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier:
-```svelte
-
+// Physical R then T positions, both with Shift
+createHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction())
```
-## Default options
+Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`.
-```svelte
-
+```ts
+createHotkeys([
+ { hotkey: ['G', 'G'], callback: () => scrollToTop() },
+ { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() },
+ { hotkey: ['D', 'D'], callback: () => deleteLine() },
+ { hotkey: ['D', 'W'], callback: () => deleteWord() },
+ { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() },
+])
```
-### `meta`
+## Displaying sequences
-Sequences support the same `meta` option as hotkeys, so you can attach a `name` and `description` for use in shortcut palettes and devtools.
+`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default:
```ts
-createHotkeySequence(['G', 'G'], () => scrollToTop(), {
- meta: { name: 'Go to Top', description: 'Scroll to the top of the page' },
-})
+formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C'
+formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G'
```
-See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations.
-
-## Chained modifier chords
-
-This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters.
+Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys).
-You can use the same modifier on consecutive steps (for example `Shift+R` then `Shift+T`):
-
-```ts
-createHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction())
-```
+## How sequences work
-While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored: they do not advance the sequence and do not reset progress.
+The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed:
-## Common patterns
+1. It checks whether the key matches the next expected step of each sequence
+2. If it matches, that sequence advances
+3. If the timeout expires between steps, the sequence resets
+4. When all steps are completed, the callback fires
-### Vim-style navigation
+Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks.
-```ts
-createHotkeySequence(['G', 'G'], () => scrollToTop())
-createHotkeySequence(['G', 'Shift+G'], () => scrollToBottom())
-createHotkeySequence(['D', 'D'], () => deleteLine())
-createHotkeySequence(['D', 'W'], () => deleteWord())
-createHotkeySequence(['C', 'I', 'W'], () => changeInnerWord())
-```
+Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes.
-### Konami Code
+### Conflicting registrations
-```ts
-createHotkeySequence(
- ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'B', 'A'],
- () => enableEasterEgg(),
- { timeout: 2000 },
-)
-```
+For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts.
-## Under the hood
+## Standalone matching
-`createHotkeySequence` uses the singleton `SequenceManager`. You can also access it directly:
+For sequence matching without the singleton manager, use `createSequenceMatcher`:
```ts
-import {
- createSequenceMatcher,
- getSequenceManager,
-} from '@tanstack/svelte-hotkeys'
+import { createSequenceMatcher } from '@tanstack/svelte-hotkeys'
-const manager = getSequenceManager()
const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 })
-```
-
-### Conflicting registrations
-For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap.
+document.addEventListener('keydown', (e) => {
+ if (matcher.match(e)) {
+ console.log('Sequence completed!')
+ }
+ console.log('Progress:', matcher.getProgress()) // e.g., 1
+})
+```
diff --git a/docs/framework/svelte/quick-start.md b/docs/framework/svelte/quick-start.md
index 2ef2c748..066280a9 100644
--- a/docs/framework/svelte/quick-start.md
+++ b/docs/framework/svelte/quick-start.md
@@ -79,9 +79,10 @@ The `Mod` modifier automatically resolves to `Meta` (Command) on macOS and `Cont
```svelte
@@ -155,18 +156,21 @@ Use `setHotkeysContext` when you want defaults for a subtree. This is an advance
import { setHotkeysContext } from '@tanstack/svelte-hotkeys'
setHotkeysContext({
- hotkey: { preventDefault: true },
- hotkeySequence: { timeout: 1500 },
+ defaultOptions: { hotkey: { preventDefault: true, timeout: 1500 } },
})
```
+## Declaring app hotkeys
+
+To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `HotkeysProvider` or `setHotkeysContext`. See the [App Hotkeys Guide](./guides/app-hotkeys).
+
## Next steps
- [Hotkeys Guide](./guides/hotkeys)
- [Sequences Guide](./guides/sequences)
- [Hotkey Recording Guide](./guides/hotkey-recording)
-- [Sequence Recording Guide](./guides/sequence-recording)
+- [App Hotkeys Guide](./guides/app-hotkeys)
- [Key State Tracking Guide](./guides/key-state-tracking)
- [Formatting & Display Guide](./guides/formatting-display)
diff --git a/docs/framework/svelte/reference/functions/createHotkey.md b/docs/framework/svelte/reference/functions/createHotkey.md
index 025eba12..aa155b36 100644
--- a/docs/framework/svelte/reference/functions/createHotkey.md
+++ b/docs/framework/svelte/reference/functions/createHotkey.md
@@ -3,43 +3,112 @@ id: createHotkey
title: createHotkey
---
+## Call Signature
+
```ts
-function createHotkey(
+function createHotkey(
hotkey,
callback,
options?): void;
```
-Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:56](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L56)
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L62)
+
+Svelte function for registering a hotkey on the document: a chord such as
+`'Mod+S'`, a sequence such as `['G', 'G']`, or an app hotkey from
+`defineAppHotkeys`. Pass getters for reactive values. Use
+`createHotkeyAttachment` for element-scoped hotkeys.
+
+Chords accept `requireReset`; sequences accept `timeout`. A one-step
+sequence registers as its chord. An app hotkey uses its effective hotkey
+from `HotkeysProvider` (including the user's custom hotkey) and its
+declared meta and options, and registers nothing while unbound.
+
+### Type Parameters
+
+#### T
-Register a global hotkey for the current component.
+`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>
-## Parameters
+### Parameters
-### hotkey
+#### hotkey
-`MaybeGetter`\<`RegisterableHotkey`\>
+`MaybeGetter`\<`T`\>
-### callback
+#### callback
`HotkeyCallback`
-### options?
+#### options?
-`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}`
+`MaybeGetter`\<[`CreateAppHotkeyOptions`](../type-aliases/CreateAppHotkeyOptions.md)\<`T`\>\>
-## Returns
+### Returns
`void`
-## Example
+### Example
```svelte
+```
+
+## Call Signature
+
+```ts
+function createHotkey(
+ hotkey,
+ callback,
+ options?): void;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L67)
+
+Svelte function for registering a hotkey on the document: a chord such as
+`'Mod+S'`, a sequence such as `['G', 'G']`, or an app hotkey from
+`defineAppHotkeys`. Pass getters for reactive values. Use
+`createHotkeyAttachment` for element-scoped hotkeys.
+
+Chords accept `requireReset`; sequences accept `timeout`. A one-step
+sequence registers as its chord. An app hotkey uses its effective hotkey
+from `HotkeysProvider` (including the user's custom hotkey) and its
+declared meta and options, and registers nothing while unbound.
- createHotkey('Mod+S', () => {
- console.log('Mod+S pressed')
- })
+### Type Parameters
+
+#### T
+
+`T` *extends* `RegisterableHotkey`
+
+### Parameters
+
+#### hotkey
+
+`MaybeGetter`\<`T`\>
+
+#### callback
+
+`HotkeyCallback`
+
+#### options?
+
+`MaybeGetter`\<`AdapterOptionsFor`\<`T`, [`CreateHotkeyTarget`](../type-aliases/CreateHotkeyTarget.md)\>\>
+
+### Returns
+
+`void`
+
+### Example
+
+```svelte
+
```
diff --git a/docs/framework/svelte/reference/functions/createHotkeyAttachment.md b/docs/framework/svelte/reference/functions/createHotkeyAttachment.md
index fa0775cf..1cc82d33 100644
--- a/docs/framework/svelte/reference/functions/createHotkeyAttachment.md
+++ b/docs/framework/svelte/reference/functions/createHotkeyAttachment.md
@@ -3,49 +3,98 @@ id: createHotkeyAttachment
title: createHotkeyAttachment
---
+## Call Signature
+
```ts
-function createHotkeyAttachment(
+function createHotkeyAttachment(
hotkey,
callback,
options?): Attachment;
```
-Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:130](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L130)
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:93](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L93)
+
+Create an attachment for an element-scoped hotkey: a chord, a sequence, or
+an app hotkey that listens on the attached element.
+
+### Type Parameters
+
+#### T
-Create an attachment for element-scoped hotkeys.
+`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>
-## Parameters
+### Parameters
-### hotkey
+#### hotkey
-`MaybeGetter`\<`RegisterableHotkey`\>
+`MaybeGetter`\<`T`\>
-### callback
+#### callback
`HotkeyCallback`
-### options?
+#### options?
-`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}`
+`MaybeGetter`\<[`CreateAppHotkeyOptions`](../type-aliases/CreateAppHotkeyOptions.md)\<`T`\>\>
-## Returns
+### Returns
`Attachment`\<`HTMLElement`\>
-## Example
+### Example
```svelte
+
+
Editor content
+```
+
+## Call Signature
+
+```ts
+function createHotkeyAttachment(
+ hotkey,
+ callback,
+options?): Attachment;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L98)
+
+Create an attachment for an element-scoped hotkey: a chord, a sequence, or
+an app hotkey that listens on the attached element.
- let count = $state(0)
- const saveHotkey = createHotkeyAttachment('Mod+S', () => {
- count++
- })
+### Type Parameters
+#### T
+
+`T` *extends* `RegisterableHotkey`
+
+### Parameters
+
+#### hotkey
+
+`MaybeGetter`\<`T`\>
+
+#### callback
+
+`HotkeyCallback`
+
+#### options?
+
+`MaybeGetter`\<`AdapterOptionsFor`\<`T`, [`CreateHotkeyTarget`](../type-aliases/CreateHotkeyTarget.md)\>\>
+
+### Returns
+
+`Attachment`\<`HTMLElement`\>
+
+### Example
+
+```svelte
+
-
- Count: {count}
-
+
Editor content
```
diff --git a/docs/framework/svelte/reference/functions/createHotkeyRecorder.md b/docs/framework/svelte/reference/functions/createHotkeyRecorder.md
index 265d7e7a..70c2fa31 100644
--- a/docs/framework/svelte/reference/functions/createHotkeyRecorder.md
+++ b/docs/framework/svelte/reference/functions/createHotkeyRecorder.md
@@ -3,18 +3,192 @@ id: createHotkeyRecorder
title: createHotkeyRecorder
---
+## Call Signature
+
+```ts
+function createHotkeyRecorder(options): SvelteHotkeyRecorder;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:102](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L102)
+
+Svelte function for recording keyboard shortcuts. Options may be a getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+`HotkeyChordRecorderOptions`
+
+### Returns
+
+[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)\<`HotkeyChord`\>
+
+### Example
+
+```svelte
+
+
+
+```
+
+## Call Signature
+
+```ts
+function createHotkeyRecorder(options): SvelteHotkeyRecorder;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L105)
+
+Svelte function for recording keyboard shortcuts. Options may be a getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+`HotkeySequenceRecorderOptions`
+
+### Returns
+
+[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)
+
+### Example
+
+```svelte
+
+
+
+```
+
+## Call Signature
+
```ts
function createHotkeyRecorder(options): SvelteHotkeyRecorder;
```
-Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:101](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L101)
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:110](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L110)
+
+Svelte function for recording keyboard shortcuts. Options may be a getter.
-## Parameters
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
-### options
+### Parameters
-`MaybeGetter`\<`HotkeyRecorderOptions`\>
+#### options
-## Returns
+() => `HotkeySequenceRecorderOptions`
+
+### Returns
[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)
+
+### Example
+
+```svelte
+
+
+
+```
+
+## Call Signature
+
+```ts
+function createHotkeyRecorder(options): SvelteHotkeyRecorder;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:113](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L113)
+
+Svelte function for recording keyboard shortcuts. Options may be a getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+() => `HotkeyChordRecorderOptions`
+
+### Returns
+
+[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)\<`HotkeyChord`\>
+
+### Example
+
+```svelte
+
+
+
+```
+
+## Call Signature
+
+```ts
+function createHotkeyRecorder(options): SvelteHotkeyRecorder;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:116](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L116)
+
+Svelte function for recording keyboard shortcuts. Options may be a getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+() => `HotkeyRecorderOptions`
+
+### Returns
+
+[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)
+
+### Example
+
+```svelte
+
+
+
+```
diff --git a/docs/framework/svelte/reference/functions/createHotkeySequence.md b/docs/framework/svelte/reference/functions/createHotkeySequence.md
deleted file mode 100644
index 7cca3164..00000000
--- a/docs/framework/svelte/reference/functions/createHotkeySequence.md
+++ /dev/null
@@ -1,70 +0,0 @@
----
-id: createHotkeySequence
-title: createHotkeySequence
----
-
-```ts
-function createHotkeySequence(
- sequence,
- callback,
- options?): void;
-```
-
-Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:72](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L72)
-
-Register a global keyboard shortcut sequence for the current component.
-
-Each step may include modifiers. You can chain the same modifier across
-steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
-Control, Alt, or Meta pressed alone) are ignored while matching—they do not
-advance the sequence or reset progress.
-
-## Parameters
-
-### sequence
-
-`MaybeGetter`\<`HotkeySequence`\>
-
-### callback
-
-`HotkeyCallback`
-
-### options?
-
-`MaybeGetter`\<[`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)\> = `{}`
-
-## Returns
-
-`void`
-
-## Example
-
-```svelte
-
-
-
-```
diff --git a/docs/framework/svelte/reference/functions/createHotkeys.md b/docs/framework/svelte/reference/functions/createHotkeys.md
index 2fef9b8c..5d309363 100644
--- a/docs/framework/svelte/reference/functions/createHotkeys.md
+++ b/docs/framework/svelte/reference/functions/createHotkeys.md
@@ -7,19 +7,25 @@ title: createHotkeys
function createHotkeys(hotkeys, commonOptions?): void;
```
-Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L65)
+Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:128](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L128)
-Register multiple global hotkeys for the current component.
+Svelte function for registering several hotkeys at once on the document:
+chords, sequences, and app hotkeys. Pass getters for reactive values. Use
+`createHotkeysAttachment` for element-scoped hotkeys.
+
+Options merge in this order: provider defaults, app hotkey declaration,
+commonOptions, per-hotkey options. Each option applies only to the kinds
+that support it. Unbound app hotkeys and empty sequences register nothing.
## Parameters
### hotkeys
-`MaybeGetter`\<[`CreateHotkeyDefinition`](../interfaces/CreateHotkeyDefinition.md)[]\>
+`MaybeGetter`\
### commonOptions?
-`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}`
+`MaybeGetter`\<[`CreateHotkeyCommonOptions`](../type-aliases/CreateHotkeyCommonOptions.md)\> = `{}`
## Returns
@@ -29,12 +35,9 @@ Register multiple global hotkeys for the current component.
```svelte
```
diff --git a/docs/framework/svelte/reference/functions/createHotkeysAttachment.md b/docs/framework/svelte/reference/functions/createHotkeysAttachment.md
index b90e5943..9be741b2 100644
--- a/docs/framework/svelte/reference/functions/createHotkeysAttachment.md
+++ b/docs/framework/svelte/reference/functions/createHotkeysAttachment.md
@@ -7,19 +7,20 @@ title: createHotkeysAttachment
function createHotkeysAttachment(hotkeys, commonOptions?): Attachment;
```
-Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:188](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L188)
+Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:152](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L152)
-Create an attachment for element-scoped multi-hotkey registration.
+Create an attachment for element-scoped hotkeys: chords, sequences, and app
+hotkeys listen on the attached element.
## Parameters
### hotkeys
-`MaybeGetter`\<[`CreateHotkeyDefinition`](../interfaces/CreateHotkeyDefinition.md)[]\>
+`MaybeGetter`\
### commonOptions?
-`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}`
+`MaybeGetter`\<[`CreateHotkeyCommonOptions`](../type-aliases/CreateHotkeyCommonOptions.md)\> = `{}`
## Returns
@@ -29,15 +30,11 @@ Create an attachment for element-scoped multi-hotkey registration.
```svelte
-
- Editor content
-
+
Editor content
```
diff --git a/docs/framework/svelte/reference/functions/getAppHotkeyList.md b/docs/framework/svelte/reference/functions/getAppHotkeyList.md
new file mode 100644
index 00000000..223dfb0e
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getAppHotkeyList.md
@@ -0,0 +1,18 @@
+---
+id: getAppHotkeyList
+title: getAppHotkeyList
+---
+
+```ts
+function getAppHotkeyList(): SvelteAppHotkeyList;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts#L63)
+
+Lists every app hotkey with its live status, for cheat sheets and settings
+screens. App hotkeys whose components have not mounted are `'inactive'`;
+those without a hotkey are `'unbound'`.
+
+## Returns
+
+[`SvelteAppHotkeyList`](../interfaces/SvelteAppHotkeyList.md)
diff --git a/docs/framework/svelte/reference/functions/getAppHotkeys.md b/docs/framework/svelte/reference/functions/getAppHotkeys.md
new file mode 100644
index 00000000..c2f7cfc2
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getAppHotkeys.md
@@ -0,0 +1,26 @@
+---
+id: getAppHotkeys
+title: getAppHotkeys
+---
+
+```ts
+function getAppHotkeys(): SvelteAppHotkeys;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts#L41)
+
+Returns reactive access to the effective app hotkeys from `HotkeysProvider`,
+typed by `Register`.
+
+## Returns
+
+[`SvelteAppHotkeys`](../interfaces/SvelteAppHotkeys.md)
+
+## Example
+
+```svelte
+
+```
diff --git a/docs/framework/svelte/reference/functions/getCustomHotkeys.md b/docs/framework/svelte/reference/functions/getCustomHotkeys.md
new file mode 100644
index 00000000..7a0ea8cc
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getCustomHotkeys.md
@@ -0,0 +1,18 @@
+---
+id: getCustomHotkeys
+title: getCustomHotkeys
+---
+
+```ts
+function getCustomHotkeys(): SvelteCustomHotkeys;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:101](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts#L101)
+
+Reads and changes the user's custom hotkeys. Changes apply to every
+registered app hotkey immediately and are passed to the provider's
+`onCustomHotkeysChange`, ready to save.
+
+## Returns
+
+[`SvelteCustomHotkeys`](../interfaces/SvelteCustomHotkeys.md)
diff --git a/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md b/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md
index 6faf5355..f09464c0 100644
--- a/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md
+++ b/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md
@@ -7,7 +7,7 @@ title: getDefaultHotkeysOptions
function getDefaultHotkeysOptions(): HotkeysProviderOptions;
```
-Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L59)
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:110](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L110)
## Returns
diff --git a/docs/framework/svelte/reference/functions/getHotkeyHint.md b/docs/framework/svelte/reference/functions/getHotkeyHint.md
index 835014ac..99f9eda9 100644
--- a/docs/framework/svelte/reference/functions/getHotkeyHint.md
+++ b/docs/framework/svelte/reference/functions/getHotkeyHint.md
@@ -7,19 +7,21 @@ title: getHotkeyHint
function getHotkeyHint(hotkey, options?): SvelteHotkeyHint;
```
-Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L18)
+Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L28)
-Returns an object with a reactive `visible` getter indicating whether held modifiers reveal this shortcut.
-Pass getters to track changing bindings and options.
-Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires
-every modifier. Combine with the action's enabled state before showing a badge.
-This helper does not register a shortcut or check whether its target is focused.
+Returns an object with a reactive `visible` getter indicating whether held
+modifiers reveal this shortcut. Pass getters to track changing hotkeys and
+options. A sequence reveals its hint from its first step, and an app hotkey
+uses its effective hotkey (false while unbound). This helper does not
+register a shortcut or check whether its target is focused.
## Parameters
### hotkey
-`MaybeGetter`\<`RegisterableHotkey`\>
+`MaybeGetter`\<
+ \| `RegisterableHotkey`
+ \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>\>
### options?
diff --git a/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md b/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md
index 892d2a43..6879ded4 100644
--- a/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md
+++ b/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md
@@ -7,28 +7,25 @@ title: getHotkeyRegistrations
function getHotkeyRegistrations(): SvelteHotkeyRegistrations;
```
-Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L58)
+Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L39)
-Svelte function that returns reactive access to all hotkey and sequence
-registrations from the singleton managers.
+Svelte function that returns reactive access to every hotkey registration,
+chords and sequences, from the singleton HotkeyManager.
-This is a standalone function that does NOT require the HotkeysProvider.
+This function does NOT require the HotkeysProvider.
## Returns
[`SvelteHotkeyRegistrations`](../interfaces/SvelteHotkeyRegistrations.md)
-Object with reactive `hotkeys` and `sequences` properties
-
## Example
```svelte
-
-{#each registrations.hotkeys as reg}
-
+
+```
+
+An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered.
+
+## Customize and persist hotkeys
+
+TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change:
+
+```vue
+
+
+
+
+
+
+
+```
+
+The provider emits `customHotkeysChange` with the complete custom hotkeys after every change. With `provideHotkeysContext`, pass `customHotkeys` and `onCustomHotkeysChange` in its options instead.
+
+- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them.
+- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning.
+- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile.
+
+Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others.
+
+### Change hotkeys
+
+`useCustomHotkeys` reads and changes the user's custom hotkeys:
+
+```vue
+
+
+
+
+
+```
+
+| Member | Description |
+| --- | --- |
+| `customHotkeys.value` | The hotkeys that differ from the defaults |
+| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. |
+| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` |
+| `resetCustomHotkey(id)` | Restores one default |
+| `resetCustomHotkeys()` | Restores every default |
+
+Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered.
+
+Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it.
diff --git a/docs/framework/vue/guides/formatting-display.md b/docs/framework/vue/guides/formatting-display.md
index 46c42dfc..f23ab316 100644
--- a/docs/framework/vue/guides/formatting-display.md
+++ b/docs/framework/vue/guides/formatting-display.md
@@ -3,7 +3,7 @@ title: Formatting & Display Guide
id: formatting-display
---
-Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string.
+Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string.
## Format a binding
@@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true })
With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`.
-Sequences are arrays of bindings. Format their steps individually:
+## Format sequences
-```ts
-import type { HotkeySequence } from '@tanstack/vue-hotkeys'
+Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default:
-const sequence: HotkeySequence = ['Mod+[KeyK]', 'C']
-const label = sequence.map((step) => formatForDisplay(step)).join(' → ')
+```ts
+formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C'
+formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G'
+formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true })
+// [['⌘', 'K'], ['C']] — one array of keycaps per step
```
-`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels.
+With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps.
## Choose symbols and separators
@@ -63,7 +65,7 @@ formatForDisplay('Control++', {
}) // 'Ctrl · +'
```
-An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`.
+An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps.
## Supply layout labels
@@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', {
The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only.
+## Compare hotkeys
+
+`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord.
+
## Parse and store bindings
`parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity:
@@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S'
Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field.
-Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts).
+Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts).
Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay).
diff --git a/docs/framework/vue/guides/hotkey-recording.md b/docs/framework/vue/guides/hotkey-recording.md
index 62a8b7e5..2493ab55 100644
--- a/docs/framework/vue/guides/hotkey-recording.md
+++ b/docs/framework/vue/guides/hotkey-recording.md
@@ -3,119 +3,138 @@ title: Hotkey Recording Guide
id: hotkey-recording
---
-TanStack Hotkeys provides the `useHotkeyRecorder` composable for building shortcut customization UIs in Vue.
+`useHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too.
Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead.
-TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release.
-
-## Reactive options
-
-Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update.
+TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release.
## Basic usage
```vue
-
-
-
-
+
```
-## Return value
+## Recorder state and actions
+
+| Member | Description |
+| --- | --- |
+| `isRecording` | Whether the recorder is currently listening for key presses |
+| `steps` | Chords captured so far in a multi-step recording |
+| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded |
+| `startRecording()` | Start listening for key presses |
+| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` |
+| `cancelRecording()` | Stop listening and call `onCancel` |
+| `commitRecording()` | Commit the current `steps` now (multi-step recordings) |
-- `isRecording`: reactive ref-like value indicating whether recording is active
-- `recordedHotkey`: reactive ref-like value with the most recently recorded hotkey
-- `startRecording()`: start listening for key presses
-- `stopRecording()`: stop listening and reset recorder state without calling `onRecord`
-- `cancelRecording()`: stop listening and discard the in-progress recording
+State members are readonly refs. Destructure them at the top level of `
+### `onCancel`
-
-
-
-
-
-```
+Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`.
+
+### `onClear`
+
+Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default.
### `ignoreInputs`
-The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when an input has focus. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements.
+Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements.
+
+Options can also be a ref or getter, such as `useHotkeyRecorder(() => ({ maxSteps: 3, recordBy: mode.value, onRecord }))`, so recording policy can follow reactive state.
+
+### Default options
+
+Set defaults for every recorder with `HotkeysProvider` (or `provideHotkeysContext` in a root component's setup). Per-recorder options override them.
+
+```vue
+
+
+
+```
+
+## Recording sequences
+
+Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step:
```ts
-useHotkeyRecorder({
- ignoreInputs: false, // record even from inside inputs
- onRecord: (hotkey) => console.log(hotkey),
+const recorder = useHotkeyRecorder({
+ maxSteps: 3,
+ onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]']
})
+
+// Live preview while recording
+const label = computed(() =>
+ recorder.isRecording.value
+ ? formatForDisplay(recorder.steps.value) || 'Press chords, then Enter…'
+ : formatForDisplay(recorder.recordedHotkey.value ?? []),
+)
```
+A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit.
+
+| Option | Description |
+| --- | --- |
+| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. |
+| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. |
+| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. |
+
+`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord.
+
## Recording behavior
-- Modifier-only presses do not complete a recording.
-- Modifier plus key combinations record the full shortcut.
-- Escape cancels recording.
-- Backspace and Delete clear the shortcut.
-- Recorded values are normalized to portable `Mod` format.
+| Key | Behavior |
+| --- | --- |
+| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step |
+| Modifier + key (e.g., Ctrl+S) | Records the full combination |
+| Single key (e.g., Escape, F1) | Records the single key |
+| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps |
+| Escape | Cancels the recording |
+| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` |
+
+Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows.
## Validation and conflicts
-Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`:
+The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`:
```ts
const options = {
detectConflicts: {
- // Use registration IDs to exclude the binding being edited:
+ // Use registration IDs to exclude the hotkey being edited:
excludeIds: [registrationId],
target: document,
eventType: 'keydown',
@@ -129,8 +148,63 @@ const options = {
}
```
-`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity.
+`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace.
+
+`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute.
+
+For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position.
+
+## Building a shortcut settings UI
+
+For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place.
+
+For a single component, ordinary state is enough:
+
+```vue
+
+
+
+
+
+```
-`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch.
+## Under the hood
-For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position.
+`useHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state as refs, and destroys it when the effect scope is disposed.
diff --git a/docs/framework/vue/guides/hotkeys.md b/docs/framework/vue/guides/hotkeys.md
index 87e178c9..b60854af 100644
--- a/docs/framework/vue/guides/hotkeys.md
+++ b/docs/framework/vue/guides/hotkeys.md
@@ -5,6 +5,18 @@ id: hotkeys
The `useHotkey` composable is the primary way to register keyboard shortcuts in Vue applications. It wraps the singleton `HotkeyManager` with automatic cleanup, support for template refs, and reactive option syncing.
+## Chords and sequences
+
+A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`.
+
+```ts
+useHotkey('Mod+S', () => save()) // chord
+useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // sequence
+useHotkey('Escape', () => close(), { requireReset: true }) // chord-only option
+```
+
+Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them.
+
## Logical keys and physical positions
Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position:
@@ -252,39 +264,31 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro
## Introspecting registrations
-Use the `useHotkeyRegistrations` composable to get a live view of all hotkey and sequence registrations. It works well for shortcut palettes, help dialogs, and devtools.
+Use the `useHotkeyRegistrations` composable to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs.
```vue
-
```
-The returned `hotkeys` array contains registration objects with the hotkey string, options (including `meta`), and enabled state. The `sequences` array contains sequence registrations with the same structure.
+The returned ref holds one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel.
+
+Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys).
## The hotkey manager
diff --git a/docs/framework/vue/guides/key-state-tracking.md b/docs/framework/vue/guides/key-state-tracking.md
index 8dd69fe3..3f2c44fe 100644
--- a/docs/framework/vue/guides/key-state-tracking.md
+++ b/docs/framework/vue/guides/key-state-tracking.md
@@ -105,6 +105,6 @@ tracker.isKeyHeld('Shift')
const visible = useHotkeyHint(() => binding.value) // computed ref: visible.value
```
-For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection.
+For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound).
Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`.
diff --git a/docs/framework/vue/guides/sequence-recording.md b/docs/framework/vue/guides/sequence-recording.md
deleted file mode 100644
index 5815ebfa..00000000
--- a/docs/framework/vue/guides/sequence-recording.md
+++ /dev/null
@@ -1,111 +0,0 @@
----
-title: Sequence Recording Guide
-id: sequence-recording
----
-
-TanStack Hotkeys provides the `useHotkeySequenceRecorder` composable for building UIs where users record multi-chord sequences (Vim-style shortcuts). The recorder captures each step like a single hotkey chord. Users finish with Enter by default, or you can use manual commit and an optional idle timeout.
-
-Sequence recording uses `recordBy: 'code'` by default, preserving every step as a physical string such as `['[KeyG]', 'Alt+[KeyS]']`. Set `recordBy: 'key'` for logical characters. The shared rejection and conflict options follow the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts).
-
-TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release.
-
-## Reactive options
-
-Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update.
-
-## Basic usage
-
-```vue
-
-
-
-
-
-
-
-
-```
-
-## Return value
-
-| Property | Type | Description |
-|----------|------|-------------|
-| `isRecording` | `boolean` | Whether the recorder is listening |
-| `steps` | `HotkeySequence` | Chords captured in the current session |
-| `recordedSequence` | `HotkeySequence \| null` | Last committed sequence |
-| `startRecording` | `() => void` | Start a new session |
-| `stopRecording` | `() => void` | Stop without calling `onRecord` |
-| `cancelRecording` | `() => void` | Stop and call `onCancel` |
-| `commitRecording` | `() => void` | Commit current `steps` (no-op if empty) |
-
-## Options
-
-`recordBy` defaults to `'code'`; choose `'key'` for logical characters. Repeats and IME composition never append steps. Modifier-only presses wait for a complete chord.
-
-Core options live on `HotkeySequenceRecorderOptions` from `@tanstack/hotkeys`:
-
-- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`.
-- `onCancel`, `onClear`: same intent as the hotkey recorder.
-- `commitKeys`: `'enter'` (default) or `'none'`. With `'none'`, only `commitRecording()` (or `idleTimeoutMs`) finishes recording; plain Enter can be recorded as a chord.
-- `commitOnEnter`: when `commitKeys` is `'enter'`, set to `false` to treat Enter as a normal chord (then use `commitRecording()` or idle timeout to finish).
-- `idleTimeoutMs`: optional milliseconds of inactivity after the last completed chord to auto-commit. The timer does not run while waiting for the first chord.
-
-### Provider defaults
-
-```vue
-
-
-
-```
-
-### `ignoreInputs`
-
-The `HotkeySequenceRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when an input has focus. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements.
-
-## Validation and conflicts
-
-Sequence `validate(sequence, { events, parsedSequence })` runs when committing and returns true, false, or a rejection message. `detectConflicts` checks sequence prefixes as well as single bindings. Rejected commits keep recording active with the steps intact so the user can edit them with Backspace. Empty Backspace/Delete clears and emits `onClear` only; removing a nonempty step does not commit. Recorded steps, commit keys, and releases are isolated from application handlers.
-
-## Behavior
-
-| Input | Behavior |
-|-------|----------|
-| Valid chord | Appended to `steps`; listener stays active |
-| Enter (no modifiers), `commitKeys: 'enter'`, `steps.length >= 1` | Commits and calls `onRecord` |
-| Escape | Cancels; `onCancel` |
-| Backspace / Delete (no modifiers) | Removes last step, or if empty runs only `onClear` and stops |
-
-Recorded chords use portable `Mod` format, same as `HotkeyRecorder`.
-
-## Under the hood
-
-`useHotkeySequenceRecorder` wraps the `HotkeySequenceRecorder` class and subscribes to its TanStack Store, same pattern as `useHotkeyRecorder`.
diff --git a/docs/framework/vue/guides/sequences.md b/docs/framework/vue/guides/sequences.md
index a176f839..0c53f195 100644
--- a/docs/framework/vue/guides/sequences.md
+++ b/docs/framework/vue/guides/sequences.md
@@ -3,148 +3,145 @@ title: Sequences Guide
id: sequences
---
-TanStack Hotkeys supports multi-key sequences in Vue, where keys are pressed one after another rather than simultaneously.
+A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `useHotkey` and `useHotkeys`, list them with `useHotkeyRegistrations`, record them with `useHotkeyRecorder`, and format them with `formatForDisplay`.
-Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`.
-
-## Reactive options
-
-Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same.
+Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`.
## Basic usage
+Pass an array of chords wherever you pass a hotkey:
+
```vue
```
+The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`.
+
## Many sequences at once
-When you need several sequences, or a reactive list whose length changes, use `useHotkeySequences` instead of many `useHotkeySequence` calls. One composable registers every sequence safely.
+`useHotkeys` registers chords and sequences together, including dynamic lists:
-```vue
-
```
-Options merge like `useHotkeys`: `HotkeysProvider` defaults, then `commonOptions`, then each definition's `options`.
-
-## Matching steps
-
-Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches.
-
## Sequence options
-```ts
-useHotkeySequence(['G', 'G'], callback, {
- timeout: 1000,
- enabled: true,
-})
-```
+Sequences accept the same options as chords, except `requireReset`, plus `timeout`.
-### Reactive `enabled`
+### `timeout`
-When disabled, the sequence stays registered (visible in devtools); only execution is suppressed.
-
-```vue
-
+// Slow sequence - user has more time between keys
+useHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 })
```
-## Global defaults via provider
+`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider` (or `provideHotkeysContext` in a root component's setup):
```vue
-
-
-
-
+
+
```
-### `meta`
+### `enabled` and `meta`
-Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` to use in shortcut palettes and devtools.
+These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys).
```ts
-useHotkeySequence(['G', 'G'], () => scrollToTop(), {
- meta: { name: 'Go to Top', description: 'Scroll to the top of the page' },
-})
+useHotkey(['G', 'G'], () => scrollToTop(), () => ({
+ enabled: isVimMode.value,
+ meta: { name: 'Go to top', description: 'Scroll to the top of the page' },
+}))
```
-See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations.
-
-## Chained modifier chords
+## Sequences with modifiers
-This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters.
-
-Each step can use modifiers (for example `Mod+K` then `Mod+C`). You can use the same modifier on consecutive steps:
+Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier:
```ts
-useHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction())
-```
+// Ctrl+K followed by Ctrl+C (VS Code-style comment)
+useHotkey(['Mod+K', 'Mod+C'], () => commentSelection())
-While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored: they do not advance the sequence and do not reset progress. A user can press Shift alone between `Shift+R` and `Shift+T` without breaking the sequence.
+// Physical R then T positions, both with Shift
+useHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction())
+```
-## Common patterns
+Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`.
-### Vim-style navigation
+## Vim-style navigation
```ts
-useHotkeySequence(['G', 'G'], () => scrollToTop())
-useHotkeySequence(['G', 'Shift+G'], () => scrollToBottom())
-useHotkeySequence(['D', 'D'], () => deleteLine())
-useHotkeySequence(['D', 'W'], () => deleteWord())
-useHotkeySequence(['C', 'I', 'W'], () => changeInnerWord())
+useHotkeys([
+ { hotkey: ['G', 'G'], callback: () => scrollToTop() },
+ { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() },
+ { hotkey: ['D', 'D'], callback: () => deleteLine() },
+ { hotkey: ['D', 'W'], callback: () => deleteWord() },
+ { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() },
+])
```
-### Konami Code
+## Displaying sequences
+
+`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default:
```ts
-useHotkeySequence(
- ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'B', 'A'],
- () => enableEasterEgg(),
- { timeout: 2000 },
-)
+formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C'
+formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G'
```
-## Under the hood
+Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys).
-`useHotkeySequence` uses the singleton `SequenceManager`. You can also access it directly:
+## How sequences work
-```ts
-import { createSequenceMatcher, getSequenceManager } from '@tanstack/vue-hotkeys'
+The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed:
-const manager = getSequenceManager()
-const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 })
-```
+1. It checks whether the key matches the next expected step of each sequence
+2. If it matches, that sequence advances
+3. If the timeout expires between steps, the sequence resets
+4. When all steps are completed, the callback fires
+
+Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks.
+
+Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes.
### Conflicting registrations
-For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap.
+For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts.
+
+## Standalone matching
+
+For sequence matching without the singleton manager, use `createSequenceMatcher`:
+
+```ts
+import { createSequenceMatcher } from '@tanstack/vue-hotkeys'
+
+const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 })
+
+document.addEventListener('keydown', (e) => {
+ if (matcher.match(e)) {
+ console.log('Sequence completed!')
+ }
+ console.log('Progress:', matcher.getProgress()) // e.g., 1
+})
+```
diff --git a/docs/framework/vue/quick-start.md b/docs/framework/vue/quick-start.md
index 2e53388f..597955b9 100644
--- a/docs/framework/vue/quick-start.md
+++ b/docs/framework/vue/quick-start.md
@@ -81,14 +81,15 @@ useHotkey('Escape', () => {
```vue
```
-For several sequences or a list that changes at runtime, prefer a single `useHotkeySequences([...])` call (see the [Sequences guide](./guides/sequences.md#many-sequences-at-once)).
+For several sequences or a list that changes at runtime, prefer a single `useHotkeys([...])` call (see the [Sequences guide](./guides/sequences.md#many-sequences-at-once)).
### Tracking held keys
@@ -136,8 +137,7 @@ import { HotkeysProvider } from '@tanstack/vue-hotkeys'
@@ -146,11 +146,15 @@ import { HotkeysProvider } from '@tanstack/vue-hotkeys'
```
+## Declaring app hotkeys
+
+To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `HotkeysProvider`. See the [App Hotkeys Guide](./guides/app-hotkeys).
+
## Next steps
- [Hotkeys Guide](./guides/hotkeys)
- [Sequences Guide](./guides/sequences)
- [Hotkey Recording Guide](./guides/hotkey-recording)
-- [Sequence Recording Guide](./guides/sequence-recording)
+- [App Hotkeys Guide](./guides/app-hotkeys)
- [Key State Tracking Guide](./guides/key-state-tracking)
- [Formatting & Display Guide](./guides/formatting-display)
diff --git a/docs/framework/vue/reference/functions/provideHotkeysContext.md b/docs/framework/vue/reference/functions/provideHotkeysContext.md
index 43d888ed..66e33512 100644
--- a/docs/framework/vue/reference/functions/provideHotkeysContext.md
+++ b/docs/framework/vue/reference/functions/provideHotkeysContext.md
@@ -4,17 +4,32 @@ title: provideHotkeysContext
---
```ts
-function provideHotkeysContext(defaultOptions?): void;
+function provideHotkeysContext(options?): void;
```
-Defined in: [HotkeysProviderContext.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L26)
+Defined in: [HotkeysProviderContext.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L69)
+
+Provides default options, app hotkeys, and the user's custom hotkeys to
+descendants. `HotkeysProvider` calls this; call it directly in a root
+component's setup instead of rendering the provider.
## Parameters
-### defaultOptions?
+### options?
-`MaybeRefOrGetter`\<[`HotkeysProviderOptions`](../interfaces/HotkeysProviderOptions.md)\>
+[`ProvideHotkeysContextOptions`](../interfaces/ProvideHotkeysContextOptions.md) = `{}`
## Returns
`void`
+
+## Example
+
+```ts
+provideHotkeysContext({
+ appHotkeys,
+ customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'),
+ onCustomHotkeysChange: (custom) =>
+ localStorage.setItem('hotkeys', JSON.stringify(custom)),
+})
+```
diff --git a/docs/framework/vue/reference/functions/useAppHotkeyList.md b/docs/framework/vue/reference/functions/useAppHotkeyList.md
new file mode 100644
index 00000000..ee133fee
--- /dev/null
+++ b/docs/framework/vue/reference/functions/useAppHotkeyList.md
@@ -0,0 +1,18 @@
+---
+id: useAppHotkeyList
+title: useAppHotkeyList
+---
+
+```ts
+function useAppHotkeyList(): ComputedRef>[]>;
+```
+
+Defined in: [useAppHotkeys.ts:48](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L48)
+
+Lists every app hotkey with its live status, in declaration order, for
+cheat sheets and settings screens. App hotkeys whose components have not
+mounted are `'inactive'`; those without a hotkey are `'unbound'`.
+
+## Returns
+
+`ComputedRef`\<`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[]\>
diff --git a/docs/framework/vue/reference/functions/useAppHotkeys.md b/docs/framework/vue/reference/functions/useAppHotkeys.md
new file mode 100644
index 00000000..b93e4f9e
--- /dev/null
+++ b/docs/framework/vue/reference/functions/useAppHotkeys.md
@@ -0,0 +1,24 @@
+---
+id: useAppHotkeys
+title: useAppHotkeys
+---
+
+```ts
+function useAppHotkeys(): ComputedRef>>>;
+```
+
+Defined in: [useAppHotkeys.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L35)
+
+Returns a computed ref of the effective app hotkeys from `HotkeysProvider`,
+typed by `Register`: each declaration with the user's custom hotkey applied.
+
+## Returns
+
+`ComputedRef`\<`Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\>\>
+
+## Example
+
+```ts
+const hotkeys = useAppHotkeys()
+useHotkey(() => hotkeys.value['doc.save'], () => save())
+```
diff --git a/docs/framework/vue/reference/functions/useCustomHotkeys.md b/docs/framework/vue/reference/functions/useCustomHotkeys.md
new file mode 100644
index 00000000..53c966ac
--- /dev/null
+++ b/docs/framework/vue/reference/functions/useCustomHotkeys.md
@@ -0,0 +1,18 @@
+---
+id: useCustomHotkeys
+title: useCustomHotkeys
+---
+
+```ts
+function useCustomHotkeys(): UseCustomHotkeysResult;
+```
+
+Defined in: [useAppHotkeys.ts:87](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L87)
+
+Reads and changes the user's custom hotkeys. Changes apply to every
+registered app hotkey immediately and are emitted by the provider as
+`custom-hotkeys-change`, ready to save.
+
+## Returns
+
+[`UseCustomHotkeysResult`](../interfaces/UseCustomHotkeysResult.md)
diff --git a/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md b/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md
index 1f316c09..8e184ce5 100644
--- a/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md
+++ b/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md
@@ -7,7 +7,7 @@ title: useDefaultHotkeysOptions
function useDefaultHotkeysOptions(): HotkeysProviderOptions;
```
-Defined in: [HotkeysProviderContext.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L42)
+Defined in: [HotkeysProviderContext.ts:109](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L109)
## Returns
diff --git a/docs/framework/vue/reference/functions/useHotkey.md b/docs/framework/vue/reference/functions/useHotkey.md
index e48c0333..0b6a8e5b 100644
--- a/docs/framework/vue/reference/functions/useHotkey.md
+++ b/docs/framework/vue/reference/functions/useHotkey.md
@@ -3,93 +3,106 @@ id: useHotkey
title: useHotkey
---
+## Call Signature
+
```ts
-function useHotkey(
+function useHotkey(
hotkey,
callback,
options?): void;
```
-Defined in: [useHotkey.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L99)
-
-Vue composable for registering a keyboard hotkey.
+Defined in: [useHotkey.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L75)
-Uses the singleton HotkeyManager for efficient event handling.
-The callback receives both the keyboard event and a context object
-containing the hotkey string and parsed hotkey.
+Vue composable for registering a hotkey: a chord such as `'Mod+S'`, a
+sequence such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`.
+The hotkey and options may be refs or getters.
-This composable automatically tracks reactive dependencies and updates
-the registration when options or the callback change.
+Chords accept `requireReset`; sequences accept `timeout`. A one-step
+sequence registers as its chord. An app hotkey uses its effective hotkey
+from `HotkeysProvider` (including the user's custom hotkey) and its
+declared meta and options, and registers nothing while unbound.
-## Parameters
+### Type Parameters
-### hotkey
+#### T
-`MaybeRefOrGetter`\<`RegisterableHotkey`\>
+`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>
-The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform)
+### Parameters
-### callback
+#### hotkey
-`HotkeyCallback`
+`MaybeRefOrGetter`\<`T`\>
-The function to call when the hotkey is pressed
+#### callback
-### options?
+`HotkeyCallback`
-`MaybeRefOrGetter`\<[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md)\> = `{}`
+#### options?
-Options for the hotkey behavior
+`MaybeRefOrGetter`\<[`UseAppHotkeyOptions`](../type-aliases/UseAppHotkeyOptions.md)\<`T`\>\>
-## Returns
+### Returns
`void`
-## Examples
+### Example
-```vue
-
+```ts
+function useHotkey(
+ hotkey,
+ callback,
+ options?): void;
```
-```vue
-
-```
+Chords accept `requireReset`; sequences accept `timeout`. A one-step
+sequence registers as its chord. An app hotkey uses its effective hotkey
+from `HotkeysProvider` (including the user's custom hotkey) and its
+declared meta and options, and registers nothing while unbound.
+
+### Type Parameters
-```vue
-
+### Parameters
-
-
...
-
+#### hotkey
+
+`MaybeRefOrGetter`\<`T`\>
+
+#### callback
+
+`HotkeyCallback`
+
+#### options?
+
+`MaybeRefOrGetter`\<[`UseHotkeyOptionsFor`](../type-aliases/UseHotkeyOptionsFor.md)\<`T`\>\>
+
+### Returns
+
+`void`
+
+### Example
+
+```ts
+useHotkey('Mod+S', () => save())
+useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 })
+useHotkey('Escape', () => close(), { enabled: isOpen })
```
diff --git a/docs/framework/vue/reference/functions/useHotkeyHint.md b/docs/framework/vue/reference/functions/useHotkeyHint.md
index 5d982332..dda117d5 100644
--- a/docs/framework/vue/reference/functions/useHotkeyHint.md
+++ b/docs/framework/vue/reference/functions/useHotkeyHint.md
@@ -7,19 +7,21 @@ title: useHotkeyHint
function useHotkeyHint(hotkey, options?): ComputedRef;
```
-Defined in: [useHotkeyHint.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyHint.ts#L14)
+Defined in: [useHotkeyHint.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyHint.ts#L24)
Returns a computed ref indicating whether held modifiers reveal this shortcut.
-Pass refs or getters to track changing bindings and options.
-Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires
-every modifier. Combine with the action's enabled state before showing a badge.
-This helper does not register a shortcut or check whether its target is focused.
+Pass refs or getters to track changing hotkeys and options. A sequence
+reveals its hint from its first step, and an app hotkey uses its effective
+hotkey (false while unbound). This helper does not register a shortcut or
+check whether its target is focused.
## Parameters
### hotkey
-`MaybeRefOrGetter`\<`RegisterableHotkey`\>
+`MaybeRefOrGetter`\<
+ \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>
+ \| `RegisterableHotkey`\>
### options?
diff --git a/docs/framework/vue/reference/functions/useHotkeyRecorder.md b/docs/framework/vue/reference/functions/useHotkeyRecorder.md
index bdd20bf6..566ee7eb 100644
--- a/docs/framework/vue/reference/functions/useHotkeyRecorder.md
+++ b/docs/framework/vue/reference/functions/useHotkeyRecorder.md
@@ -3,60 +3,162 @@ id: useHotkeyRecorder
title: useHotkeyRecorder
---
+## Call Signature
+
+```ts
+function useHotkeyRecorder(options): VueHotkeyRecorder;
+```
+
+Defined in: [useHotkeyRecorder.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L46)
+
+Vue composable for recording keyboard shortcuts. Options may be a ref or getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+`HotkeyChordRecorderOptions`
+
+### Returns
+
+[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)\<`HotkeyChord`\>
+
+### Example
+
+```ts
+const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) })
+const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save })
+```
+
+## Call Signature
+
+```ts
+function useHotkeyRecorder(options): VueHotkeyRecorder;
+```
+
+Defined in: [useHotkeyRecorder.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L49)
+
+Vue composable for recording keyboard shortcuts. Options may be a ref or getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+`HotkeySequenceRecorderOptions`
+
+### Returns
+
+[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)
+
+### Example
+
+```ts
+const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) })
+const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save })
+```
+
+## Call Signature
+
```ts
function useHotkeyRecorder(options): VueHotkeyRecorder;
```
-Defined in: [useHotkeyRecorder.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L62)
+Defined in: [useHotkeyRecorder.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L54)
-Vue composable for recording keyboard shortcuts.
+Vue composable for recording keyboard shortcuts. Options may be a ref or getter.
-This composable provides a thin wrapper around the framework-agnostic `HotkeyRecorder`
-class, managing all the complexity of capturing keyboard events, converting them
-to hotkey strings, and handling edge cases like Escape to cancel or Backspace/Delete
-to clear.
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
-## Parameters
+### Parameters
-### options
+#### options
-`MaybeRefOrGetter`\<`HotkeyRecorderOptions`\>
+() => `HotkeySequenceRecorderOptions`
-Configuration options for the recorder
+### Returns
-## Returns
+[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)
+
+### Example
+
+```ts
+const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) })
+const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save })
+```
+
+## Call Signature
+
+```ts
+function useHotkeyRecorder(options): VueHotkeyRecorder;
+```
+
+Defined in: [useHotkeyRecorder.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L57)
+
+Vue composable for recording keyboard shortcuts. Options may be a ref or getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+() => `HotkeyChordRecorderOptions`
+
+### Returns
+
+[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)\<`HotkeyChord`\>
+
+### Example
+
+```ts
+const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) })
+const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save })
+```
+
+## Call Signature
+
+```ts
+function useHotkeyRecorder(options): VueHotkeyRecorder;
+```
+
+Defined in: [useHotkeyRecorder.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L60)
+
+Vue composable for recording keyboard shortcuts. Options may be a ref or getter.
+
+With the default `maxSteps: 1`, the first valid chord is recorded. With a
+larger `maxSteps`, each chord adds a step; the recording commits on Enter
+(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
+`onRecord` receives a chord (one step) or a sequence.
+
+### Parameters
+
+#### options
+
+`Ref`\<`HotkeyRecorderOptions`\>
+
+### Returns
[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)
-An object with recording state and control functions
-
-## Example
-
-```vue
-
-
-
-
-
-
- Recording: {{ recorder.recordedHotkey }}
-
-
-
+### Example
+
+```ts
+const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) })
+const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save })
```
diff --git a/docs/framework/vue/reference/functions/useHotkeyRegistrations.md b/docs/framework/vue/reference/functions/useHotkeyRegistrations.md
index 202549f4..a4c0a020 100644
--- a/docs/framework/vue/reference/functions/useHotkeyRegistrations.md
+++ b/docs/framework/vue/reference/functions/useHotkeyRegistrations.md
@@ -4,20 +4,31 @@ title: useHotkeyRegistrations
---
```ts
-function useHotkeyRegistrations(): HotkeyRegistrationsResult;
+function useHotkeyRegistrations(): Readonly>;
```
-Defined in: [useHotkeyRegistrations.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L33)
+Defined in: [useHotkeyRegistrations.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L26)
-Vue composable that reactively reads all hotkey and sequence registrations
-from the singleton managers.
+Vue composable that reactively reads every hotkey registration, chords and
+sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
-This is a standalone composable that does NOT require the HotkeysProvider.
-It subscribes to both HotkeyManager and SequenceManager stores and
-triggers reactivity when registrations change.
+This composable does NOT require the HotkeysProvider.
## Returns
-[`HotkeyRegistrationsResult`](../interfaces/HotkeyRegistrationsResult.md)
+`Readonly`\<`Ref`\<`HotkeyRegistrationView`[]\>\>
-Object with `hotkeys` and `sequences` refs
+A ref of all registrations, without callbacks
+
+## Example
+
+```vue
+
+
+
+
+```
diff --git a/docs/framework/vue/reference/functions/useHotkeySequence.md b/docs/framework/vue/reference/functions/useHotkeySequence.md
deleted file mode 100644
index d8848b38..00000000
--- a/docs/framework/vue/reference/functions/useHotkeySequence.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-id: useHotkeySequence
-title: useHotkeySequence
----
-
-```ts
-function useHotkeySequence(
- sequence,
- callback,
- options?): void;
-```
-
-Defined in: [useHotkeySequence.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L76)
-
-Vue composable for registering a keyboard shortcut sequence (Vim-style).
-
-This composable allows you to register multi-key sequences like 'g g' or 'd d'
-that trigger when the full sequence is pressed within a timeout.
-
-Each step may include modifiers. You can chain the same modifier across
-steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
-Control, Alt, or Meta pressed alone) are ignored while matching—they do not
-advance the sequence or reset progress.
-
-## Parameters
-
-### sequence
-
-`MaybeRefOrGetter`\<`HotkeySequence`\>
-
-Array of hotkey strings that form the sequence
-
-### callback
-
-`HotkeyCallback`
-
-Function to call when the sequence is completed
-
-### options?
-
-`MaybeRefOrGetter`\<[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md)\> = `{}`
-
-Options for the sequence behavior
-
-## Returns
-
-`void`
-
-## Example
-
-```vue
-
-
-
-
...
-
-```
diff --git a/docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md b/docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md
deleted file mode 100644
index 4a37a79c..00000000
--- a/docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md
+++ /dev/null
@@ -1,22 +0,0 @@
----
-id: useHotkeySequenceRecorder
-title: useHotkeySequenceRecorder
----
-
-```ts
-function useHotkeySequenceRecorder(options): VueHotkeySequenceRecorder;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L24)
-
-Vue composable for recording multi-chord sequences (Vim-style shortcuts).
-
-## Parameters
-
-### options
-
-`MaybeRefOrGetter`\<`HotkeySequenceRecorderOptions`\>
-
-## Returns
-
-[`VueHotkeySequenceRecorder`](../interfaces/VueHotkeySequenceRecorder.md)
diff --git a/docs/framework/vue/reference/functions/useHotkeySequences.md b/docs/framework/vue/reference/functions/useHotkeySequences.md
deleted file mode 100644
index cbae9270..00000000
--- a/docs/framework/vue/reference/functions/useHotkeySequences.md
+++ /dev/null
@@ -1,68 +0,0 @@
----
-id: useHotkeySequences
-title: useHotkeySequences
----
-
-```ts
-function useHotkeySequences(definitions, commonOptions?): void;
-```
-
-Defined in: [useHotkeySequences.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L68)
-
-Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).
-
-Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref
-that returns one, so you can register variable-length lists safely.
-
-Options are merged in this order:
-HotkeysProvider defaults < commonOptions < per-definition options
-
-Definitions with an empty `sequence` are skipped (no registration).
-
-## Parameters
-
-### definitions
-
-`MaybeRefOrGetter`\<[`UseHotkeySequenceDefinition`](../interfaces/UseHotkeySequenceDefinition.md)[]\>
-
-Array of sequence definitions, or a getter/ref
-
-### commonOptions?
-
-`MaybeRefOrGetter`\<[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md)\> = `{}`
-
-Shared options applied to all sequences (overridden by per-definition options)
-
-## Returns
-
-`void`
-
-## Examples
-
-```vue
-
-```
-
-```vue
-
-```
diff --git a/docs/framework/vue/reference/functions/useHotkeys.md b/docs/framework/vue/reference/functions/useHotkeys.md
index 030f4d44..673d359d 100644
--- a/docs/framework/vue/reference/functions/useHotkeys.md
+++ b/docs/framework/vue/reference/functions/useHotkeys.md
@@ -7,63 +7,35 @@ title: useHotkeys
function useHotkeys(hotkeys, commonOptions?): void;
```
-Defined in: [useHotkeys.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L73)
+Defined in: [useHotkeys.ts:90](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L90)
-Vue composable for registering multiple keyboard hotkeys at once.
+Vue composable for registering several hotkeys at once: chords, sequences,
+and app hotkeys. The list, each hotkey, and options may be refs or getters.
-Uses the singleton HotkeyManager for efficient event handling.
-Accepts a dynamic array of hotkey definitions, making it safe to use
-with variable-length lists.
-
-Options are merged in this order:
-HotkeysProvider defaults < commonOptions < per-definition options
+Options merge in this order: provider defaults, app hotkey declaration,
+commonOptions, per-hotkey options. Each option applies only to the kinds
+that support it. Unbound app hotkeys and empty sequences register nothing.
+A target that resolves to `null` skips registration until it is set.
## Parameters
### hotkeys
-`MaybeRefOrGetter`\<[`UseHotkeyDefinition`](../interfaces/UseHotkeyDefinition.md)[]\>
-
-Array of hotkey definitions to register, or a getter/ref
+`MaybeRefOrGetter`\
### commonOptions?
-`MaybeRefOrGetter`\<[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md)\> = `{}`
-
-Shared options applied to all hotkeys (overridden by per-definition options)
+`MaybeRefOrGetter`\<[`UseHotkeyCommonOptions`](../type-aliases/UseHotkeyCommonOptions.md)\> = `{}`
## Returns
`void`
-## Examples
-
-```vue
-
-```
-
-```vue
-
```
diff --git a/docs/framework/vue/reference/functions/useHotkeysContext.md b/docs/framework/vue/reference/functions/useHotkeysContext.md
index b853d9e3..9f8fa8f2 100644
--- a/docs/framework/vue/reference/functions/useHotkeysContext.md
+++ b/docs/framework/vue/reference/functions/useHotkeysContext.md
@@ -7,7 +7,7 @@ title: useHotkeysContext
function useHotkeysContext(): HotkeysContextValue | null;
```
-Defined in: [HotkeysProviderContext.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L38)
+Defined in: [HotkeysProviderContext.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L105)
## Returns
diff --git a/docs/framework/vue/reference/index.md b/docs/framework/vue/reference/index.md
index 78df6d3f..991b6187 100644
--- a/docs/framework/vue/reference/index.md
+++ b/docs/framework/vue/reference/index.md
@@ -5,14 +5,21 @@ title: "@tanstack/vue-hotkeys"
## Interfaces
-- [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md)
- [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md)
-- [UseHotkeyDefinition](interfaces/UseHotkeyDefinition.md)
-- [UseHotkeyOptions](interfaces/UseHotkeyOptions.md)
-- [UseHotkeySequenceDefinition](interfaces/UseHotkeySequenceDefinition.md)
-- [UseHotkeySequenceOptions](interfaces/UseHotkeySequenceOptions.md)
+- [ProvideHotkeysContextOptions](interfaces/ProvideHotkeysContextOptions.md)
+- [UseCustomHotkeysResult](interfaces/UseCustomHotkeysResult.md)
- [VueHotkeyRecorder](interfaces/VueHotkeyRecorder.md)
-- [VueHotkeySequenceRecorder](interfaces/VueHotkeySequenceRecorder.md)
+
+## Type Aliases
+
+- [UseAppHotkeyOptions](type-aliases/UseAppHotkeyOptions.md)
+- [UseHotkeyChordOptions](type-aliases/UseHotkeyChordOptions.md)
+- [UseHotkeyCommonOptions](type-aliases/UseHotkeyCommonOptions.md)
+- [UseHotkeyDefinition](type-aliases/UseHotkeyDefinition.md)
+- [UseHotkeyOptions](type-aliases/UseHotkeyOptions.md)
+- [UseHotkeyOptionsFor](type-aliases/UseHotkeyOptionsFor.md)
+- [UseHotkeySequenceOptions](type-aliases/UseHotkeySequenceOptions.md)
+- [UseHotkeyTarget](type-aliases/UseHotkeyTarget.md)
## Variables
@@ -21,6 +28,9 @@ title: "@tanstack/vue-hotkeys"
## Functions
- [provideHotkeysContext](functions/provideHotkeysContext.md)
+- [useAppHotkeyList](functions/useAppHotkeyList.md)
+- [useAppHotkeys](functions/useAppHotkeys.md)
+- [useCustomHotkeys](functions/useCustomHotkeys.md)
- [useDefaultHotkeysOptions](functions/useDefaultHotkeysOptions.md)
- [useHeldKeyCodes](functions/useHeldKeyCodes.md)
- [useHeldKeys](functions/useHeldKeys.md)
@@ -30,7 +40,4 @@ title: "@tanstack/vue-hotkeys"
- [useHotkeyRegistrations](functions/useHotkeyRegistrations.md)
- [useHotkeys](functions/useHotkeys.md)
- [useHotkeysContext](functions/useHotkeysContext.md)
-- [useHotkeySequence](functions/useHotkeySequence.md)
-- [useHotkeySequenceRecorder](functions/useHotkeySequenceRecorder.md)
-- [useHotkeySequences](functions/useHotkeySequences.md)
- [useKeyHold](functions/useKeyHold.md)
diff --git a/docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md
deleted file mode 100644
index 4f34bb59..00000000
--- a/docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-id: HotkeyRegistrationsResult
-title: HotkeyRegistrationsResult
----
-
-Defined in: [useHotkeyRegistrations.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L16)
-
-Return type for useHotkeyRegistrations.
-
-## Properties
-
-### hotkeys
-
-```ts
-hotkeys: Ref;
-```
-
-Defined in: [useHotkeyRegistrations.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L18)
-
-All registered hotkeys (public view, no callbacks)
-
-***
-
-### sequences
-
-```ts
-sequences: Ref;
-```
-
-Defined in: [useHotkeyRegistrations.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L20)
-
-All registered sequences
diff --git a/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md
index ef313fd5..87b23a43 100644
--- a/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md
+++ b/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md
@@ -3,44 +3,29 @@ id: HotkeysProviderOptions
title: HotkeysProviderOptions
---
-Defined in: [HotkeysProviderContext.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L10)
+Defined in: [HotkeysProviderContext.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L12)
## Properties
### hotkey?
```ts
-optional hotkey?: Partial;
+optional hotkey?: UseHotkeyCommonOptions;
```
-Defined in: [HotkeysProviderContext.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L11)
-
-***
-
-### hotkeyRecorder?
-
-```ts
-optional hotkeyRecorder?: Partial;
-```
+Defined in: [HotkeysProviderContext.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L17)
-Defined in: [HotkeysProviderContext.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L12)
+Defaults for every hotkey registered by `useHotkey` and `useHotkeys`.
+`requireReset` applies to chords and `timeout` applies to sequences.
***
-### hotkeySequence?
+### hotkeyRecorder?
```ts
-optional hotkeySequence?: Partial;
+optional hotkeyRecorder?: Partial>;
```
-Defined in: [HotkeysProviderContext.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L14)
-
-***
-
-### hotkeySequenceRecorder?
-
-```ts
-optional hotkeySequenceRecorder?: Partial;
-```
+Defined in: [HotkeysProviderContext.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L19)
-Defined in: [HotkeysProviderContext.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L13)
+Defaults for every `useHotkeyRecorder`.
diff --git a/docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md b/docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md
new file mode 100644
index 00000000..73b8db0c
--- /dev/null
+++ b/docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md
@@ -0,0 +1,70 @@
+---
+id: ProvideHotkeysContextOptions
+title: ProvideHotkeysContextOptions
+---
+
+Defined in: [HotkeysProviderContext.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L38)
+
+Options for `provideHotkeysContext`.
+
+## Properties
+
+### appHotkeys?
+
+```ts
+optional appHotkeys?: MaybeRefOrGetter<
+ | Readonly>>
+| undefined>;
+```
+
+Defined in: [HotkeysProviderContext.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L44)
+
+Every app hotkey, from `defineAppHotkeys`. Read when the context is
+provided; later changes update the declarations.
+
+***
+
+### customHotkeys?
+
+```ts
+optional customHotkeys?:
+ | CustomHotkeys>>>
+ | null;
+```
+
+Defined in: [HotkeysProviderContext.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L49)
+
+Starting custom hotkeys, such as saved data. Read once; the context owns
+the state afterwards. Invalid entries are ignored with a warning.
+
+***
+
+### defaultOptions?
+
+```ts
+optional defaultOptions?: MaybeRefOrGetter;
+```
+
+Defined in: [HotkeysProviderContext.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L39)
+
+***
+
+### onCustomHotkeysChange?
+
+```ts
+optional onCustomHotkeysChange?: (customHotkeys) => void;
+```
+
+Defined in: [HotkeysProviderContext.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L51)
+
+Called with the custom hotkeys after every change, ready to save.
+
+#### Parameters
+
+##### customHotkeys
+
+`CustomHotkeys`
+
+#### Returns
+
+`void`
diff --git a/docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md b/docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md
new file mode 100644
index 00000000..0f8147b7
--- /dev/null
+++ b/docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md
@@ -0,0 +1,111 @@
+---
+id: UseCustomHotkeysResult
+title: UseCustomHotkeysResult
+---
+
+Defined in: [useAppHotkeys.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L60)
+
+Custom hotkey state and actions returned by `useCustomHotkeys`.
+
+## Properties
+
+### customHotkeys
+
+```ts
+customHotkeys: ComputedRef>>>>;
+```
+
+Defined in: [useAppHotkeys.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L62)
+
+The user's hotkeys that differ from the defaults.
+
+***
+
+### findConflicts
+
+```ts
+findConflicts: (id, hotkey) => AppHotkey[];
+```
+
+Defined in: [useAppHotkeys.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L76)
+
+Other app hotkeys that would collide if `id` used `hotkey`.
+
+#### Parameters
+
+##### id
+
+`string`
+
+##### hotkey
+
+`Hotkey`
+
+#### Returns
+
+`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[]
+
+***
+
+### resetCustomHotkey
+
+```ts
+resetCustomHotkey: (id) => void;
+```
+
+Defined in: [useAppHotkeys.ts:72](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L72)
+
+Restores the default hotkey for an app hotkey.
+
+#### Parameters
+
+##### id
+
+`string`
+
+#### Returns
+
+`void`
+
+***
+
+### resetCustomHotkeys
+
+```ts
+resetCustomHotkeys: () => void;
+```
+
+Defined in: [useAppHotkeys.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L74)
+
+Restores every default hotkey.
+
+#### Returns
+
+`void`
+
+***
+
+### setCustomHotkey
+
+```ts
+setCustomHotkey: (id, hotkey) => AppHotkey[];
+```
+
+Defined in: [useAppHotkeys.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useAppHotkeys.ts#L67)
+
+Sets the user's hotkey for an app hotkey. `null` unbinds it.
+Returns conflicting app hotkeys; when any exist, nothing changes.
+
+#### Parameters
+
+##### id
+
+`string`
+
+##### hotkey
+
+`Hotkey` \| `null`
+
+#### Returns
+
+`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[]
diff --git a/docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md b/docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md
deleted file mode 100644
index 718f35af..00000000
--- a/docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-id: UseHotkeyDefinition
-title: UseHotkeyDefinition
----
-
-Defined in: [useHotkeys.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L19)
-
-A single hotkey definition for use with `useHotkeys`.
-
-## Properties
-
-### callback
-
-```ts
-callback: HotkeyCallback;
-```
-
-Defined in: [useHotkeys.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L23)
-
-The function to call when the hotkey is pressed
-
-***
-
-### hotkey
-
-```ts
-hotkey: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkeys.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L21)
-
-The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object
-
-***
-
-### options?
-
-```ts
-optional options?: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkeys.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L25)
-
-Per-hotkey options (merged on top of commonOptions)
diff --git a/docs/framework/vue/reference/interfaces/UseHotkeyOptions.md b/docs/framework/vue/reference/interfaces/UseHotkeyOptions.md
deleted file mode 100644
index b668007d..00000000
--- a/docs/framework/vue/reference/interfaces/UseHotkeyOptions.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-id: UseHotkeyOptions
-title: UseHotkeyOptions
----
-
-Defined in: [useHotkey.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L17)
-
-## Extends
-
-- `Omit`\<`HotkeyOptions`, `"enabled"` \| `"target"`\>
-
-## Properties
-
-### enabled?
-
-```ts
-optional enabled?: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkey.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L26)
-
-Whether the hotkey is active.
-Can be a Ref, a getter function, or a boolean value.
-Defaults to true.
-
-***
-
-### target?
-
-```ts
-optional target?: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkey.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L32)
-
-The DOM element to attach the event listener to.
-Can be a Ref, a getter function, direct DOM element, or null.
-Defaults to document.
diff --git a/docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md b/docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md
deleted file mode 100644
index 6cd5de1a..00000000
--- a/docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-id: UseHotkeySequenceDefinition
-title: UseHotkeySequenceDefinition
----
-
-Defined in: [useHotkeySequences.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L15)
-
-A single sequence definition for use with `useHotkeySequences`.
-
-## Properties
-
-### callback
-
-```ts
-callback: HotkeyCallback;
-```
-
-Defined in: [useHotkeySequences.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L19)
-
-The function to call when the sequence is completed
-
-***
-
-### options?
-
-```ts
-optional options?: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkeySequences.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L21)
-
-Per-sequence options (merged on top of commonOptions)
-
-***
-
-### sequence
-
-```ts
-sequence: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkeySequences.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L17)
-
-Array of hotkey strings that form the sequence
diff --git a/docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md b/docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md
deleted file mode 100644
index 26c2cc58..00000000
--- a/docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-id: UseHotkeySequenceOptions
-title: UseHotkeySequenceOptions
----
-
-Defined in: [useHotkeySequence.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L12)
-
-## Extends
-
-- `Omit`\<`SequenceOptions`, `"enabled"` \| `"target"`\>
-
-## Properties
-
-### enabled?
-
-```ts
-optional enabled?: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkeySequence.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L21)
-
-Whether the sequence is active.
-Can be a Ref, a getter function, or a boolean value.
-Defaults to true.
-
-***
-
-### target?
-
-```ts
-optional target?: MaybeRefOrGetter;
-```
-
-Defined in: [useHotkeySequence.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L27)
-
-The DOM element to attach the event listener to.
-Can be a Ref, a getter function, direct DOM element, or null.
-Defaults to document.
diff --git a/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md b/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md
index bcc26a30..54b63f95 100644
--- a/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md
+++ b/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md
@@ -3,7 +3,13 @@ id: VueHotkeyRecorder
title: VueHotkeyRecorder
---
-Defined in: [useHotkeyRecorder.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L8)
+Defined in: [useHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L15)
+
+## Type Parameters
+
+### THotkey
+
+`THotkey` *extends* `Hotkey` = `Hotkey`
## Properties
@@ -13,9 +19,25 @@ Defined in: [useHotkeyRecorder.ts:8](https://github.com/TanStack/hotkeys/blob/ma
cancelRecording: () => void;
```
-Defined in: [useHotkeyRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L18)
+Defined in: [useHotkeyRecorder.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L27)
-Cancel recording without saving
+Cancel recording and call onCancel
+
+#### Returns
+
+`void`
+
+***
+
+### commitRecording
+
+```ts
+commitRecording: () => void;
+```
+
+Defined in: [useHotkeyRecorder.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L29)
+
+Commit the recorded steps now (multi-step recordings)
#### Returns
@@ -26,10 +48,10 @@ Cancel recording without saving
### isRecording
```ts
-isRecording: Ref;
+isRecording: Readonly>;
```
-Defined in: [useHotkeyRecorder.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L10)
+Defined in: [useHotkeyRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L17)
Whether recording is currently active
@@ -38,12 +60,12 @@ Whether recording is currently active
### recordedHotkey
```ts
-recordedHotkey: Ref;
+recordedHotkey: Readonly>;
```
-Defined in: [useHotkeyRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L12)
+Defined in: [useHotkeyRecorder.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L21)
-The currently recorded hotkey (for live preview)
+The last committed hotkey
***
@@ -53,7 +75,7 @@ The currently recorded hotkey (for live preview)
startRecording: () => void;
```
-Defined in: [useHotkeyRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L14)
+Defined in: [useHotkeyRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L23)
Start recording a new hotkey
@@ -63,15 +85,27 @@ Start recording a new hotkey
***
+### steps
+
+```ts
+steps: Readonly>;
+```
+
+Defined in: [useHotkeyRecorder.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L19)
+
+Chords recorded so far in a multi-step recording (for live preview)
+
+***
+
### stopRecording
```ts
stopRecording: () => void;
```
-Defined in: [useHotkeyRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L16)
+Defined in: [useHotkeyRecorder.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L25)
-Stop recording (same as cancel)
+Stop recording without calling onCancel
#### Returns
diff --git a/docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md b/docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md
deleted file mode 100644
index 5ee65750..00000000
--- a/docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md
+++ /dev/null
@@ -1,92 +0,0 @@
----
-id: VueHotkeySequenceRecorder
-title: VueHotkeySequenceRecorder
----
-
-Defined in: [useHotkeySequenceRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L11)
-
-## Properties
-
-### cancelRecording
-
-```ts
-cancelRecording: () => void;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L17)
-
-#### Returns
-
-`void`
-
-***
-
-### commitRecording
-
-```ts
-commitRecording: () => void;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L18)
-
-#### Returns
-
-`void`
-
-***
-
-### isRecording
-
-```ts
-isRecording: Ref;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L12)
-
-***
-
-### recordedSequence
-
-```ts
-recordedSequence: Ref;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L14)
-
-***
-
-### startRecording
-
-```ts
-startRecording: () => void;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L15)
-
-#### Returns
-
-`void`
-
-***
-
-### steps
-
-```ts
-steps: Ref;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L13)
-
-***
-
-### stopRecording
-
-```ts
-stopRecording: () => void;
-```
-
-Defined in: [useHotkeySequenceRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L16)
-
-#### Returns
-
-`void`
diff --git a/docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md b/docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md
new file mode 100644
index 00000000..0fe6ce72
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md
@@ -0,0 +1,18 @@
+---
+id: UseAppHotkeyOptions
+title: UseAppHotkeyOptions
+---
+
+```ts
+type UseAppHotkeyOptions = WithReactiveEnabled>;
+```
+
+Defined in: [useHotkey.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L55)
+
+Call-site options for an app hotkey, typed by its declared default kind.
+
+## Type Parameters
+
+### T
+
+`T` *extends* `AppHotkey` = `AppHotkey`
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md
new file mode 100644
index 00000000..cac8d354
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md
@@ -0,0 +1,12 @@
+---
+id: UseHotkeyChordOptions
+title: UseHotkeyChordOptions
+---
+
+```ts
+type UseHotkeyChordOptions = WithReactiveEnabled>;
+```
+
+Defined in: [useHotkey.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L36)
+
+Options for a chord such as `'Mod+S'`.
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md
new file mode 100644
index 00000000..08b4d665
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md
@@ -0,0 +1,12 @@
+---
+id: UseHotkeyCommonOptions
+title: UseHotkeyCommonOptions
+---
+
+```ts
+type UseHotkeyCommonOptions = WithReactiveEnabled>;
+```
+
+Defined in: [useHotkey.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L46)
+
+Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it.
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md b/docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md
new file mode 100644
index 00000000..98663b10
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md
@@ -0,0 +1,166 @@
+---
+id: UseHotkeyDefinition
+title: UseHotkeyDefinition
+---
+
+```ts
+type UseHotkeyDefinition =
+ | {
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+};
+```
+
+Defined in: [useHotkeys.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L36)
+
+One hotkey for `useHotkeys`. The hotkey and options may be refs or getters.
+Chords accept `requireReset`; sequences accept `timeout`; hotkeys of unknown
+kind accept only shared options.
+
+## Union Members
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+The function to call when the hotkey is pressed
+
+#### hotkey
+
+```ts
+hotkey: MaybeRefOrGetter;
+```
+
+A chord string such as `'Mod+S'`, or a RawHotkey object
+
+#### options?
+
+```ts
+optional options?: MaybeRefOrGetter;
+```
+
+Per-hotkey options (merged on top of commonOptions)
+
+***
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+#### hotkey
+
+```ts
+hotkey: MaybeRefOrGetter;
+```
+
+A sequence such as `['G', 'G']`
+
+#### options?
+
+```ts
+optional options?: MaybeRefOrGetter;
+```
+
+***
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+#### hotkey
+
+```ts
+hotkey: MaybeRefOrGetter;
+```
+
+An app hotkey from `defineAppHotkeys`
+
+#### options?
+
+```ts
+optional options?: MaybeRefOrGetter;
+```
+
+***
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: MaybeRefOrGetter;
+ options?: MaybeRefOrGetter;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+#### hotkey
+
+```ts
+hotkey: MaybeRefOrGetter;
+```
+
+A hotkey whose kind is not known statically. Accepts only shared options.
+
+#### options?
+
+```ts
+optional options?: MaybeRefOrGetter;
+```
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md
new file mode 100644
index 00000000..8286af72
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md
@@ -0,0 +1,12 @@
+---
+id: UseHotkeyOptions
+title: UseHotkeyOptions
+---
+
+```ts
+type UseHotkeyOptions = WithReactiveEnabled>;
+```
+
+Defined in: [useHotkey.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L31)
+
+Options shared by chords and sequences.
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md b/docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md
new file mode 100644
index 00000000..a9eb279f
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md
@@ -0,0 +1,18 @@
+---
+id: UseHotkeyOptionsFor
+title: UseHotkeyOptionsFor
+---
+
+```ts
+type UseHotkeyOptionsFor = WithReactiveEnabled>;
+```
+
+Defined in: [useHotkey.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L51)
+
+The options type for a hotkey: chord options for chords, sequence options for sequences.
+
+## Type Parameters
+
+### T
+
+`T` *extends* `RegisterableHotkey`
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md
new file mode 100644
index 00000000..a3ff5388
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md
@@ -0,0 +1,12 @@
+---
+id: UseHotkeySequenceOptions
+title: UseHotkeySequenceOptions
+---
+
+```ts
+type UseHotkeySequenceOptions = WithReactiveEnabled>;
+```
+
+Defined in: [useHotkey.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L41)
+
+Options for a sequence such as `['G', 'G']`.
diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md b/docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md
new file mode 100644
index 00000000..c987db81
--- /dev/null
+++ b/docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md
@@ -0,0 +1,13 @@
+---
+id: UseHotkeyTarget
+title: UseHotkeyTarget
+---
+
+```ts
+type UseHotkeyTarget = MaybeRefOrGetter;
+```
+
+Defined in: [useHotkey.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L22)
+
+The element to listen on: a ref, getter, element, or null. A target that
+resolves to `null` skips registration until it is set. Defaults to document.
diff --git a/docs/framework/vue/reference/variables/HotkeysProvider.md b/docs/framework/vue/reference/variables/HotkeysProvider.md
index 4468f772..27e2668f 100644
--- a/docs/framework/vue/reference/variables/HotkeysProvider.md
+++ b/docs/framework/vue/reference/variables/HotkeysProvider.md
@@ -5,9 +5,19 @@ title: HotkeysProvider
```ts
const HotkeysProvider: DefineComponent>>>;
+ };
+ customHotkeys: {
+ default: undefined;
+ type: PropType<
+ | CustomHotkeys>>>
+ | null>;
+ };
defaultOptions: {
default: undefined;
- type: () => HotkeysProviderOptions;
+ type: PropType;
};
}>, () =>
| VNode true;
}, string, PublicProps, ToResolvedProps>>>;
+ };
+ customHotkeys: {
+ default: undefined;
+ type: PropType<
+ | CustomHotkeys>>>
+ | null>;
+ };
defaultOptions: {
default: undefined;
- type: () => HotkeysProviderOptions;
+ type: PropType;
};
}>, {
+ customHotkeysChange: (_customHotkeys) => true;
}>, {
+ appHotkeys: Readonly>>;
+ customHotkeys: | CustomHotkeys>>>
+ | null;
defaultOptions: HotkeysProviderOptions;
}, {
}, {
@@ -32,15 +57,21 @@ const HotkeysProvider: DefineComponent;
```
-Defined in: [HotkeysProvider.tsx:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProvider.tsx#L17)
+Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProvider.tsx#L25)
-Vue component that provides default options for hotkeys context.
+Vue component that provides default options, app hotkeys, and the user's
+custom hotkeys. `customHotkeys` is read once; listen to
+`custom-hotkeys-change` to save changes.
## Example
```vue
-
+
diff --git a/docs/installation.md b/docs/installation.md
index 9de86a90..50df94a5 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -27,155 +27,99 @@ Each framework package re-exports everything from the core `@tanstack/hotkeys` p
-### Alpine
-
-Use Alpine 3.15.12 or newer within version 3. Install the optional `hotkeysPlugin` before `Alpine.start()`, or create a scope explicitly.
-
-Start with the [Quick Start](./framework/alpine/quick-start), then follow the [guides](./framework/alpine/guides/hotkeys). The package includes the hotkeys adapter; a dedicated Alpine devtools adapter is not provided.
-
-
-
-
-
-### Ember
-
-Use Ember 6.8 or newer. Register hotkeys with imported template helpers in strict-mode `.gts` components. Pass the component as owner to state readers and recorders.
-
-Start with the [Quick Start](./framework/ember/quick-start), then follow the [guides](./framework/ember/guides/hotkeys). The package includes the hotkeys adapter; a dedicated Ember devtools adapter is not provided.
-
-
-
-
-
-### Octane
-
-Use Octane 0.1.36 or newer within version 0.1, with compiler-enabled `.tsrx` components. The Octane adapter requires Node.js 22.22.2 or newer when used in Node.js.
-
-Start with the [Quick Start](./framework/octane/quick-start), then follow the [guides](./framework/octane/guides/hotkeys). The package includes the hotkeys adapter; a dedicated Octane devtools adapter is not provided.
-
-
-
-
-
-### React
+# React
Start with the [Quick Start](./framework/react/quick-start) guide. If you want the integrated devtools panel, also install:
-
-
-
-
-### Preact
+# Preact
Start with the [API reference](./framework/preact/reference/index) and [guides](./framework/preact/guides/hotkeys). If you want the integrated devtools panel, also install:
-
-
-
-
-### Solid
+# Solid
Start with the [API reference](./framework/solid/reference/index) and [guides](./framework/solid/guides/hotkeys). If you want the integrated devtools panel, also install:
-
+# Angular
-
+Start with the [Quick Start](./framework/angular/quick-start) guide and the Angular-specific [guides](./framework/angular/guides/hotkeys).
-### Svelte
+The integrated devtools panel requires Angular 21 or newer. To use it, also install:
-Start with the [Quick Start](./framework/svelte/quick-start) guide and the Svelte-specific [guides](./framework/svelte/guides/hotkeys). If you want the integrated devtools panel, also install:
+# Vue
-
+Start with the [Quick Start](./framework/vue/quick-start) guide and the Vue-specific [guides](./framework/vue/guides/hotkeys).
-
+If you want the Vue devtools panel component, also install:
-### Angular
+# Svelte
-Start with the [Quick Start](./framework/angular/quick-start) guide and the Angular-specific [guides](./framework/angular/guides/hotkeys).
+Start with the [Quick Start](./framework/svelte/quick-start) guide and the Svelte-specific [guides](./framework/svelte/guides/hotkeys). If you want the integrated devtools panel, also install:
-The integrated devtools panel requires Angular 21 or newer. To use it, also install:
+# Lit
-
+Start with the [Quick Start](./framework/lit/quick-start) guide and the Lit-specific [guides](./framework/lit/guides/hotkeys).
-
+Lit currently ships the hotkeys adapter only, so no dedicated Lit devtools package is required.
-### Vue
+# Alpine
-Start with the [Quick Start](./framework/vue/quick-start) guide and the Vue-specific [guides](./framework/vue/guides/hotkeys).
+Use Alpine 3.15.12 or newer within version 3. Install the optional `hotkeysPlugin` before `Alpine.start()`, or create a scope explicitly.
-If you want the Vue devtools panel component, also install:
+Start with the [Quick Start](./framework/alpine/quick-start), then follow the [guides](./framework/alpine/guides/hotkeys). The package includes the hotkeys adapter; a dedicated Alpine devtools adapter is not provided.
-
+# Ember
-
+Use Ember 6.8 or newer. Register hotkeys with imported template helpers in strict-mode `.gts` components. Pass the component as owner to state readers and recorders.
-### Lit
+Start with the [Quick Start](./framework/ember/quick-start), then follow the [guides](./framework/ember/guides/hotkeys). The package includes the hotkeys adapter; a dedicated Ember devtools adapter is not provided.
-Start with the [Quick Start](./framework/lit/quick-start) guide and the Lit-specific [guides](./framework/lit/guides/hotkeys).
+# Octane
-Lit currently ships the hotkeys adapter only, so no dedicated Lit devtools package is required.
+Use Octane 0.1.36 or newer within version 0.1, with compiler-enabled `.tsrx` components. The Octane adapter requires Node.js 22.22.2 or newer when used in Node.js.
+
+Start with the [Quick Start](./framework/octane/quick-start), then follow the [guides](./framework/octane/guides/hotkeys). The package includes the hotkeys adapter; a dedicated Octane devtools adapter is not provided.
-angular: @tanstack/angular-devtools
-angular: @tanstack/angular-hotkeys-devtools
-svelte: @tanstack/svelte-devtools
-svelte: @tanstack/svelte-hotkeys-devtools
-preact: @tanstack/preact-devtools
-preact: @tanstack/preact-hotkeys-devtools
react: @tanstack/react-devtools
react: @tanstack/react-hotkeys-devtools
+preact: @tanstack/preact-devtools
+preact: @tanstack/preact-hotkeys-devtools
solid: @tanstack/solid-devtools
solid: @tanstack/solid-hotkeys-devtools
+angular: @tanstack/angular-devtools
+angular: @tanstack/angular-hotkeys-devtools
vue: @tanstack/vue-hotkeys-devtools
+svelte: @tanstack/svelte-devtools
+svelte: @tanstack/svelte-hotkeys-devtools
-### React
+# React
See the [devtools](./devtools) documentation for setup details.
-
-
-
-
-### Preact
+# Preact
See the [devtools](./devtools) documentation for setup details.
-
-
-
-
-### Solid
+# Solid
See the [devtools](./devtools) documentation for setup details.
-
-
-
-
-### Vue
+# Angular
See the [devtools](./devtools) documentation for setup details.
-
-
-
-
-### Angular
+# Vue
See the [devtools](./devtools) documentation for setup details.
-
-
-
-
-### Svelte
+# Svelte
See the [devtools](./devtools) documentation for setup details.
diff --git a/docs/overview.md b/docs/overview.md
index 61779a19..a046e567 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -3,70 +3,66 @@ title: Overview
id: overview
---
-TanStack Hotkeys is a type-safe, headless library for keyboard shortcuts, sequences, recording, and key state tracking. Use its framework adapters for registration and cleanup, and build shortcut interfaces with your own components and application state.
+TanStack Hotkeys is a type-safe, framework-agnostic library for keyboard shortcuts. It includes:
-## Runtime and package requirements
+- **Hotkey registration** for shortcuts like `Mod+S` and Vim-style sequences like `G G`
+- **App hotkeys** for global cheat-sheets and user-customizable shortcuts
+- **Hotkey recording** for shortcut settings screens
+- **Key state tracking** for held keys and modifier hints
+- **Display formatting** for platform-appropriate labels like `⌘ S` and `Ctrl+S`
+- **Devtools** for inspecting shortcuts while you develop
-Hotkeys packages ship ES2022 JavaScript as ESM only and require Node.js 20 or newer when used in Node.js, except the Octane adapter, which requires Node.js 22.22.2 or newer. Browser applications need an ES2022-compatible runtime or a build pipeline that transforms the library for their supported browsers.
+> [!IMPORTANT]
+> TanStack Hotkeys is currently in **alpha** and its API is still subject to change. Early adopters are encouraged to help us solve edge cases across multiple keyboard layouts, locales, and operating systems.
-Use ESM imports, such as `import { parseHotkey } from '@tanstack/hotkeys'`. CommonJS builds and `require` export conditions are no longer provided. CommonJS applications can use dynamic `import()` or migrate their consuming modules to ESM.
+## Motivation
-Published packages contain the runtime files and TypeScript declarations in `dist`, plus package metadata, README, and license files. The repository's `src` directory and source maps are no longer included. Inspect `dist/index.d.ts` and its referenced declarations for the installed API; consult the matching repository version when you need the original implementation.
+On the surface, keyboard shortcuts are a simple concept, and you would think that it should just take a couple of lines of code to implement them. And sometimes, it can be that simple. However, there are enough small "gotchas" that can eventually add up to an annoying amount of complexity when you need to consider multiple keyboard layouts, operating systems, custom shortcuts, conflicting hotkey scopes, properly ignoring input elements, and more. Browser keyboard events have their own edge cases too: key repeat, IME composition, and keys that look stuck after a missed key-up or a window switch.
-## Choose what a shortcut follows
+As an app grows, its shortcuts also end up scattered across components. Cheat sheets and settings screens are maintained by hand, drift out of sync with the actual hotkeys, and miss shortcuts on pages that haven't loaded yet.
-A binding can follow a logical character or a physical keyboard position. Both forms use the same registration APIs:
+Surprisingly, in our experience, even AI often struggles to get hotkey management fully correct. We believe that providing a library that brings type-safety and well thought out cross-platform compatibility to hotkey management is a valuable contribution to the community.
-```ts
-// React; the other adapters accept the same binding forms.
-useHotkey('Mod+S', save) // Logical S on the active layout
-useHotkey('Alt+[KeyW]', moveForward) // Physical KeyW position
-useHotkey({ code: 'NumpadAdd', mod: true }, zoomIn)
-```
+## Features
-`Mod` means Command on macOS and Control on Windows/Linux. Brackets identify physical `event.code` names in strings, including `[Enter]` and `[F13]`. Object bindings use either `key` or `code`, never both. Both logical names and physical codes have type-safe autocomplete.
+### Hotkey registration
-Logical bindings prefer `event.key`. ASCII letter output remains authoritative on layouts such as Dvorak and AZERTY; conservative code fallback helps when Option, dead keys, or non-Latin output transforms the character. Physical bindings match `event.code` exactly. Among eligible registrations on a target, exact matches take priority over weaker fallbacks.
+Register shortcuts such as `Mod+S` to save a document or `Escape` to close a dialog. `HotkeyManager` handles event listeners, conflict detection, and input filtering. Shortcuts can be enabled conditionally or scoped to a specific element, so a dialog's shortcuts can follow the dialog's lifetime.
-Logical and physical bindings both support F1–F24 and shared named keys such as `CapsLock`, `MediaPlayPause`, and `BrowserBack`. Some browser names differ: logical `LaunchApplication1` corresponds to physical `[LaunchApp1]`. Logical `Enter` includes numpad Enter, while `[Enter]` and `[NumpadEnter]` distinguish their positions.
+By default, TanStack Hotkeys prevents the browser's default action, stops event propagation, and filters shortcuts in input elements. These options are configurable when your application needs different behavior.
-## Register actions and sequences
+### Multi-key sequences
-Single shortcuts and sequence steps use the same binding syntax. For example, `['G', 'G']` follows the logical letter, while `['[KeyG]', '[KeyG]']` follows a position. Sequences may mix logical and physical steps. Modifier-only events, IME composition, and automatic repeats do not advance sequences or extend their timeout.
+Some shortcuts are a series of keystrokes instead of a single combination. Pass an array of chords anywhere a hotkey is accepted to register a Vim-style sequence such as `['G', 'G']` or `['D', 'I', 'W']`, with a configurable timeout between steps. Use sequences for navigation or commands when you want more shortcuts without adding more modifier keys.
-Registrations support enabled state, element targets, input handling, callback metadata, and conflict policies. Framework adapters update bindings from normal reactive state and remove them on unmount. `meta.name`, `meta.description`, and `meta.group` describe registrations for menus and help panels; they do not change matching or scope.
+### App hotkeys
-## Record a replacement binding
+Hotkeys registered by components only exist while those components are mounted. Declare every hotkey up front with `defineAppHotkeys` and pass the declarations to your framework's provider to list them in cheat sheets and settings screens, even on routes that have not loaded. Users can rebind or unbind any of them, conflicts are checked across every declaration, and your app decides where to save their custom hotkeys.
-Recorders default to `recordBy: 'code'`. On macOS, Option+S producing `ß` records `Alt+[KeyS]`, so replay follows the same physical combination. Set `recordBy: 'key'` to intentionally record the produced logical character. The recorder never silently switches between these modes.
+### Hotkey recording
-Pass recorded strings directly into registrations and store them in ordinary application state. Both single and sequence recorders support validation, structured rejection, and live-registry conflict checks. Rejected candidates leave recording active. Clearing calls only `onClear`; your app decides whether that removes a binding or restores an initial value.
+`HotkeyRecorder` captures a key combination so users can customize shortcuts in a settings UI. Set `maxSteps` to let users record a sequence too. It supports validation and conflict checks before you save a hotkey. You can register the recorded hotkey directly, store it in your application's state, or save it as an app hotkey's custom hotkey.
-## Display shortcuts and hints
+### Key state tracking
-Use `formatForDisplay` at render time. Both `Mod+S` and `Mod+[KeyS]` display as `⌘ S` on macOS, but their stored identities remain distinct. `parts: true` returns individual keycap labels, and symbols can be configured separately for modifiers and keys.
+`KeyStateTracker` tracks held keys and modifiers. Framework APIs such as `useHeldKeys`, `useHeldKeyCodes`, and `useKeyHold` let you respond while a key is down. Use them for keyboard controls, visual feedback, or hints that appear while a modifier is held.
-Physical labels use readable defaults such as `S`, `2`, and numpad labels. For a known layout, pass an already-resolved `layoutMap`; use `keyLabels` for explicit overrides. Formatting stays synchronous and never loads a keyboard layout. Labels do not change registration or recording behavior.
+### Type-safe, cross-platform bindings
-Key-state primitives expose held logical keys and physical codes. `matchesHeldModifiers` and framework hint helpers reveal relevant shortcuts while modifiers are held. Live registration views and devtools expose current shortcuts and sequence progress, making it possible to build grouped help panels without a separate action catalog.
+TypeScript autocomplete helps you write valid shortcut strings such as `Control+A`, `Alt+S`, or `Mod+Shift+G`. You can also use a `RawHotkey` object, such as `{ key: 'S', mod: true }`.
-## Inspect a binding
+`Mod` resolves to Command on macOS and Control on Windows and Linux, so the same binding works across operating systems. Logical shortcuts follow the character that was pressed, while physical shortcuts follow a keyboard position. This lets you choose between a letter-based command and a position-based control, such as movement in a game.
-`ParsedHotkey` preserves identity as a union: logical bindings have `key`, physical bindings have `code`. Narrow with `parsed.code !== undefined` before reading it. Shared resolved flags and the ordered modifier list live in `ParsedModifiers`. `parseKeyboardEvent` produces logical identity; code recording constructs physical identity explicitly.
+### Display formatting
-## Framework guides
+Format chords and sequences for menus, buttons, and cheat sheets with platform-appropriate labels, such as `⌘ S` on macOS and `Ctrl+S` on Windows. You can display a shortcut as text or individual keycaps while keeping its stored hotkey separate from its label.
-| Framework | Start here |
-| --- | --- |
-| Alpine | [Quick start](./framework/alpine/quick-start) |
-| Angular | [Quick start](./framework/angular/quick-start) |
-| Ember | [Quick start](./framework/ember/quick-start) |
-| Lit | [Quick start](./framework/lit/quick-start) |
-| Octane | [Quick start](./framework/octane/quick-start) |
-| Preact | [Hotkeys guide](./framework/preact/guides/hotkeys) |
-| React | [Quick start](./framework/react/quick-start) |
-| Solid | [Hotkeys guide](./framework/solid/guides/hotkeys) |
-| Svelte | [Quick start](./framework/svelte/quick-start) |
-| Vue | [Quick start](./framework/vue/quick-start) |
+### Framework adapters
-Explore a kitchen sink for [Alpine](./framework/alpine/examples/kitchen-sink), [Ember](./framework/ember/examples/kitchen-sink), [Octane](./framework/octane/examples/kitchen-sink), or [React](./framework/react/examples/kitchen-sink) to see route lifetimes, recording, and hints. Try the [vanilla formatter playground](./framework/vanilla/examples/formatForDisplay) for display options.
+Use React and Preact hooks, Solid primitives, Angular inject APIs, Vue composables, Lit controllers and decorators, Svelte reactive APIs, Alpine scopes, Ember helpers, or Octane hooks. The adapters connect hotkey registration and key state to your framework and clean up registrations when components unmount. You can also use the core utilities directly in vanilla JavaScript.
+
+### Devtools
+
+See registered hotkeys, declared app hotkeys, held keys, and sequence progress in real time. The [devtools](./devtools) help you inspect which shortcuts are active and how they are configured while developing your application.
+
+For a complete walkthrough, see the quick start for [React](framework/react/quick-start), [Preact](framework/preact/quick-start), [Solid](framework/solid/quick-start), [Angular](framework/angular/quick-start), [Vue](framework/vue/quick-start), [Svelte](framework/svelte/quick-start), [Lit](framework/lit/quick-start), [Alpine](framework/alpine/quick-start), [Ember](framework/ember/quick-start), or [Octane](framework/octane/quick-start).
diff --git a/docs/reference/adapter/functions/createHotkeyBindings.md b/docs/reference/adapter/functions/createHotkeyBindings.md
index bb0ba927..4fdf78d1 100644
--- a/docs/reference/adapter/functions/createHotkeyBindings.md
+++ b/docs/reference/adapter/functions/createHotkeyBindings.md
@@ -7,18 +7,16 @@ title: createHotkeyBindings
function createHotkeyBindings(): object;
```
-Defined in: [adapter.ts:107](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L107)
+Defined in: [adapter.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L53)
Reconciles adapter registrations while retaining handles for option updates.
## Returns
-`object`
-
-### destroy
+### destroy()
```ts
-destroy: () => void = bindings.destroy;
+destroy(): void;
```
#### Returns
@@ -31,15 +29,18 @@ destroy: () => void = bindings.destroy;
update(definitions, commonOptions?): void;
```
+Registers new definitions, updates existing ones in place, and removes
+stale ones. Definitions with an empty sequence or a null target are skipped.
+
#### Parameters
##### definitions
-[`HotkeyDefinition`](../interfaces/HotkeyDefinition.md)[]
+readonly [`HotkeyDefinition`](../type-aliases/HotkeyDefinition.md)[]
##### commonOptions?
-`HotkeyOptions` = `{}`
+[`HotkeyBindingsCommonOptions`](../type-aliases/HotkeyBindingsCommonOptions.md) = `{}`
#### Returns
diff --git a/docs/reference/adapter/functions/createHotkeySequenceBindings.md b/docs/reference/adapter/functions/createHotkeySequenceBindings.md
deleted file mode 100644
index f97db585..00000000
--- a/docs/reference/adapter/functions/createHotkeySequenceBindings.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-id: createHotkeySequenceBindings
-title: createHotkeySequenceBindings
----
-
-```ts
-function createHotkeySequenceBindings(): object;
-```
-
-Defined in: [adapter.ts:143](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L143)
-
-Reconciles sequence bindings; empty sequences and explicit null targets are skipped.
-
-## Returns
-
-`object`
-
-### destroy
-
-```ts
-destroy: () => void = bindings.destroy;
-```
-
-#### Returns
-
-`void`
-
-### update()
-
-```ts
-update(definitions, commonOptions?): void;
-```
-
-#### Parameters
-
-##### definitions
-
-[`HotkeySequenceDefinition`](../interfaces/HotkeySequenceDefinition.md)[]
-
-##### commonOptions?
-
-`SequenceOptions` = `{}`
-
-#### Returns
-
-`void`
diff --git a/docs/reference/adapter/functions/prepareHotkeyDefinitions.md b/docs/reference/adapter/functions/prepareHotkeyDefinitions.md
new file mode 100644
index 00000000..a410cc1a
--- /dev/null
+++ b/docs/reference/adapter/functions/prepareHotkeyDefinitions.md
@@ -0,0 +1,36 @@
+---
+id: prepareHotkeyDefinitions
+title: prepareHotkeyDefinitions
+---
+
+```ts
+function prepareHotkeyDefinitions(definitions, options?): HotkeyDefinition[];
+```
+
+Defined in: [adapter.ts:280](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L280)
+
+Converts adapter definitions into `createHotkeyBindings` definitions.
+Options merge in this order: defaults, app hotkey declaration, common
+options, per-definition options. App hotkeys resolve their effective hotkey
+from the store, and an unbound app hotkey keeps its position but registers
+nothing.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
+
+## Parameters
+
+### definitions
+
+readonly [`AdapterHotkeyDefinition`](../type-aliases/AdapterHotkeyDefinition.md)\<`TTarget`\>[]
+
+### options?
+
+[`PrepareHotkeyDefinitionsOptions`](../interfaces/PrepareHotkeyDefinitionsOptions.md)\<`TTarget`\> = `{}`
+
+## Returns
+
+[`HotkeyDefinition`](../type-aliases/HotkeyDefinition.md)[]
diff --git a/docs/reference/adapter/functions/subscribePublishedAppHotkeys.md b/docs/reference/adapter/functions/subscribePublishedAppHotkeys.md
new file mode 100644
index 00000000..778aa996
--- /dev/null
+++ b/docs/reference/adapter/functions/subscribePublishedAppHotkeys.md
@@ -0,0 +1,25 @@
+---
+id: subscribePublishedAppHotkeys
+title: subscribePublishedAppHotkeys
+---
+
+```ts
+function subscribePublishedAppHotkeys(onChange): () => void;
+```
+
+Defined in: [adapter.ts:319](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L319)
+
+Calls `onChange` when the store published with `publishAppHotkeysStore` or
+`provideAppHotkeys` changes, or when its app hotkeys or custom hotkeys
+change. For adapters that resolve app hotkeys without a provider. Returns an
+unsubscribe function.
+
+## Parameters
+
+### onChange
+
+() => `void`
+
+## Returns
+
+() => `void`
diff --git a/docs/reference/adapter/index.md b/docs/reference/adapter/index.md
index fec56d9d..17967895 100644
--- a/docs/reference/adapter/index.md
+++ b/docs/reference/adapter/index.md
@@ -5,10 +5,23 @@ title: "@tanstack/hotkeys"
## Interfaces
-- [HotkeyDefinition](interfaces/HotkeyDefinition.md)
-- [HotkeySequenceDefinition](interfaces/HotkeySequenceDefinition.md)
+- [PrepareHotkeyDefinitionsOptions](interfaces/PrepareHotkeyDefinitionsOptions.md)
+
+## Type Aliases
+
+- [AdapterAppHotkeyOptions](type-aliases/AdapterAppHotkeyOptions.md)
+- [AdapterChordOptions](type-aliases/AdapterChordOptions.md)
+- [AdapterCommonOptions](type-aliases/AdapterCommonOptions.md)
+- [AdapterHotkeyDefinition](type-aliases/AdapterHotkeyDefinition.md)
+- [AdapterHotkeyOptions](type-aliases/AdapterHotkeyOptions.md)
+- [AdapterOptionsFor](type-aliases/AdapterOptionsFor.md)
+- [AdapterSequenceOptions](type-aliases/AdapterSequenceOptions.md)
+- [HotkeyBindingsCommonOptions](type-aliases/HotkeyBindingsCommonOptions.md)
+- [HotkeyDefinition](type-aliases/HotkeyDefinition.md)
+- [WithTarget](type-aliases/WithTarget.md)
## Functions
- [createHotkeyBindings](functions/createHotkeyBindings.md)
-- [createHotkeySequenceBindings](functions/createHotkeySequenceBindings.md)
+- [prepareHotkeyDefinitions](functions/prepareHotkeyDefinitions.md)
+- [subscribePublishedAppHotkeys](functions/subscribePublishedAppHotkeys.md)
diff --git a/docs/reference/adapter/interfaces/HotkeyDefinition.md b/docs/reference/adapter/interfaces/HotkeyDefinition.md
deleted file mode 100644
index 17cd68d5..00000000
--- a/docs/reference/adapter/interfaces/HotkeyDefinition.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-id: HotkeyDefinition
-title: HotkeyDefinition
----
-
-Defined in: [adapter.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L14)
-
-A hotkey and its callback, with optional per-binding options.
-
-## Properties
-
-### callback
-
-```ts
-callback: HotkeyCallback;
-```
-
-Defined in: [adapter.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L16)
-
-***
-
-### hotkey
-
-```ts
-hotkey: RegisterableHotkey;
-```
-
-Defined in: [adapter.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L15)
-
-***
-
-### options?
-
-```ts
-optional options?: HotkeyOptions;
-```
-
-Defined in: [adapter.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L17)
diff --git a/docs/reference/adapter/interfaces/HotkeySequenceDefinition.md b/docs/reference/adapter/interfaces/HotkeySequenceDefinition.md
deleted file mode 100644
index d09fe726..00000000
--- a/docs/reference/adapter/interfaces/HotkeySequenceDefinition.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-id: HotkeySequenceDefinition
-title: HotkeySequenceDefinition
----
-
-Defined in: [adapter.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L21)
-
-A sequence and its callback, with optional per-binding options.
-
-## Properties
-
-### callback
-
-```ts
-callback: HotkeyCallback;
-```
-
-Defined in: [adapter.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L23)
-
-***
-
-### options?
-
-```ts
-optional options?: SequenceOptions;
-```
-
-Defined in: [adapter.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L24)
-
-***
-
-### sequence
-
-```ts
-sequence: HotkeySequence;
-```
-
-Defined in: [adapter.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L22)
diff --git a/docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md b/docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md
new file mode 100644
index 00000000..8be14089
--- /dev/null
+++ b/docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md
@@ -0,0 +1,76 @@
+---
+id: PrepareHotkeyDefinitionsOptions
+title: PrepareHotkeyDefinitionsOptions
+---
+
+Defined in: [adapter.ts:258](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L258)
+
+Context for `prepareHotkeyDefinitions`.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
+
+## Properties
+
+### appHotkeysStore?
+
+```ts
+optional appHotkeysStore?:
+ | AppHotkeysStore>>>
+ | null;
+```
+
+Defined in: [adapter.ts:260](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L260)
+
+The provider's app hotkeys store, so app hotkeys use custom hotkeys.
+
+***
+
+### commonOptions?
+
+```ts
+optional commonOptions?: AdapterCommonOptions;
+```
+
+Defined in: [adapter.ts:264](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L264)
+
+Options for every definition, applied after app hotkey declarations.
+
+***
+
+### defaults?
+
+```ts
+optional defaults?: AdapterCommonOptions;
+```
+
+Defined in: [adapter.ts:262](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L262)
+
+Provider defaults, applied first.
+
+***
+
+### resolveTarget?
+
+```ts
+optional resolveTarget?: (target) => Target | null | undefined;
+```
+
+Defined in: [adapter.ts:270](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L270)
+
+Resolves a framework target, such as a ref. Return `undefined` for the
+document or `null` to skip registration until the target exists.
+Without it, targets pass through and `null` means document.
+
+#### Parameters
+
+##### target
+
+`TTarget`
+
+#### Returns
+
+`Target` \| `null` \| `undefined`
diff --git a/docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md b/docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md
new file mode 100644
index 00000000..f48fcd23
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md
@@ -0,0 +1,26 @@
+---
+id: AdapterAppHotkeyOptions
+title: AdapterAppHotkeyOptions
+---
+
+```ts
+type AdapterAppHotkeyOptions = Omit<[T["defaultHotkey"]] extends [HotkeySequence] ? AdapterSequenceOptions : [T["defaultHotkey"]] extends [HotkeyChord] ? AdapterChordOptions : AdapterCommonOptions, "meta">;
+```
+
+Defined in: [adapter.ts:209](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L209)
+
+Call-site options for an app hotkey. `meta` comes from the declaration.
+Kind-specific options follow the declared default: `requireReset` for a
+chord, `timeout` for a sequence, both for an app hotkey without a default.
+If the user rebinds it to the other kind, options for the old kind no
+longer apply.
+
+## Type Parameters
+
+### T
+
+`T` *extends* `AppHotkey`
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/adapter/type-aliases/AdapterChordOptions.md b/docs/reference/adapter/type-aliases/AdapterChordOptions.md
new file mode 100644
index 00000000..a7cc7590
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterChordOptions.md
@@ -0,0 +1,18 @@
+---
+id: AdapterChordOptions
+title: AdapterChordOptions
+---
+
+```ts
+type AdapterChordOptions = WithTarget;
+```
+
+Defined in: [adapter.ts:176](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L176)
+
+Options for a chord such as `'Mod+S'`, with a framework target.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/adapter/type-aliases/AdapterCommonOptions.md b/docs/reference/adapter/type-aliases/AdapterCommonOptions.md
new file mode 100644
index 00000000..7f233152
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterCommonOptions.md
@@ -0,0 +1,18 @@
+---
+id: AdapterCommonOptions
+title: AdapterCommonOptions
+---
+
+```ts
+type AdapterCommonOptions = AdapterChordOptions & AdapterSequenceOptions;
+```
+
+Defined in: [adapter.ts:188](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L188)
+
+Options applied to every hotkey, such as defaults. Each applies to the kinds that support it.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md b/docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md
new file mode 100644
index 00000000..84143253
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md
@@ -0,0 +1,174 @@
+---
+id: AdapterHotkeyDefinition
+title: AdapterHotkeyDefinition
+---
+
+```ts
+type AdapterHotkeyDefinition =
+ | {
+ callback: HotkeyCallback;
+ hotkey: RegisterableHotkeyChord;
+ options?: AdapterChordOptions;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: HotkeySequence;
+ options?: AdapterSequenceOptions;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: AppHotkey;
+ options?: Omit, "meta">;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: RegisterableHotkey;
+ options?: AdapterHotkeyOptions;
+};
+```
+
+Defined in: [adapter.ts:223](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L223)
+
+One hotkey and its callback for adapter APIs such as `useHotkeys`. Chords
+accept `requireReset`; sequences accept `timeout`; hotkeys of unknown kind
+accept only shared options.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
+
+## Union Members
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: RegisterableHotkeyChord;
+ options?: AdapterChordOptions;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+The function to call when the hotkey is pressed
+
+#### hotkey
+
+```ts
+hotkey: RegisterableHotkeyChord;
+```
+
+A chord string such as `'Mod+S'`, or a RawHotkey object
+
+#### options?
+
+```ts
+optional options?: AdapterChordOptions;
+```
+
+Per-hotkey options (merged on top of common options)
+
+***
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: HotkeySequence;
+ options?: AdapterSequenceOptions;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+#### hotkey
+
+```ts
+hotkey: HotkeySequence;
+```
+
+A sequence such as `['G', 'G']`
+
+#### options?
+
+```ts
+optional options?: AdapterSequenceOptions;
+```
+
+***
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: AppHotkey;
+ options?: Omit, "meta">;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+#### hotkey
+
+```ts
+hotkey: AppHotkey;
+```
+
+An app hotkey from `defineAppHotkeys`. Kind-specific options apply
+only while its effective hotkey has that kind.
+
+#### options?
+
+```ts
+optional options?: Omit, "meta">;
+```
+
+***
+
+### Type Literal
+
+```ts
+{
+ callback: HotkeyCallback;
+ hotkey: RegisterableHotkey;
+ options?: AdapterHotkeyOptions;
+}
+```
+
+#### callback
+
+```ts
+callback: HotkeyCallback;
+```
+
+#### hotkey
+
+```ts
+hotkey: RegisterableHotkey;
+```
+
+A hotkey whose kind is not known statically, such as user data typed
+`Hotkey`. Accepts only shared options.
+
+#### options?
+
+```ts
+optional options?: AdapterHotkeyOptions;
+```
diff --git a/docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md b/docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md
new file mode 100644
index 00000000..9a329287
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md
@@ -0,0 +1,18 @@
+---
+id: AdapterHotkeyOptions
+title: AdapterHotkeyOptions
+---
+
+```ts
+type AdapterHotkeyOptions = WithTarget;
+```
+
+Defined in: [adapter.ts:173](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L173)
+
+Options shared by chords and sequences, with a framework target.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/adapter/type-aliases/AdapterOptionsFor.md b/docs/reference/adapter/type-aliases/AdapterOptionsFor.md
new file mode 100644
index 00000000..ba2d9e60
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterOptionsFor.md
@@ -0,0 +1,22 @@
+---
+id: AdapterOptionsFor
+title: AdapterOptionsFor
+---
+
+```ts
+type AdapterOptionsFor = [T] extends [never] ? AdapterCommonOptions : T extends readonly [unknown] ? AdapterChordOptions : T extends ReadonlyArray ? AdapterSequenceOptions : AdapterChordOptions;
+```
+
+Defined in: [adapter.ts:192](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L192)
+
+Chord options for chords, sequence options for sequences.
+
+## Type Parameters
+
+### T
+
+`T` *extends* `RegisterableHotkey`
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/adapter/type-aliases/AdapterSequenceOptions.md b/docs/reference/adapter/type-aliases/AdapterSequenceOptions.md
new file mode 100644
index 00000000..275050f8
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/AdapterSequenceOptions.md
@@ -0,0 +1,18 @@
+---
+id: AdapterSequenceOptions
+title: AdapterSequenceOptions
+---
+
+```ts
+type AdapterSequenceOptions = WithTarget;
+```
+
+Defined in: [adapter.ts:182](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L182)
+
+Options for a sequence such as `['G', 'G']`, with a framework target.
+
+## Type Parameters
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md b/docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md
new file mode 100644
index 00000000..f659186d
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md
@@ -0,0 +1,12 @@
+---
+id: HotkeyBindingsCommonOptions
+title: HotkeyBindingsCommonOptions
+---
+
+```ts
+type HotkeyBindingsCommonOptions = HotkeyChordOptions & HotkeySequenceOptions;
+```
+
+Defined in: [adapter.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L47)
+
+Options shared by every definition. Each option applies only to the kinds that support it.
diff --git a/docs/reference/adapter/type-aliases/HotkeyDefinition.md b/docs/reference/adapter/type-aliases/HotkeyDefinition.md
new file mode 100644
index 00000000..c3f5bf94
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/HotkeyDefinition.md
@@ -0,0 +1,23 @@
+---
+id: HotkeyDefinition
+title: HotkeyDefinition
+---
+
+```ts
+type HotkeyDefinition =
+ | {
+ callback: HotkeyCallback;
+ hotkey: RegisterableHotkeyChord;
+ options?: HotkeyChordOptions;
+}
+ | {
+ callback: HotkeyCallback;
+ hotkey: HotkeySequence;
+ options?: HotkeySequenceOptions;
+};
+```
+
+Defined in: [adapter.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L34)
+
+A hotkey and its callback, with optional per-binding options. Chords accept
+`requireReset`; sequences accept `timeout`.
diff --git a/docs/reference/adapter/type-aliases/WithTarget.md b/docs/reference/adapter/type-aliases/WithTarget.md
new file mode 100644
index 00000000..d1b87c21
--- /dev/null
+++ b/docs/reference/adapter/type-aliases/WithTarget.md
@@ -0,0 +1,32 @@
+---
+id: WithTarget
+title: WithTarget
+---
+
+```ts
+type WithTarget = Omit & object;
+```
+
+Defined in: [adapter.ts:164](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L164)
+
+Options with a framework-specific target, such as a ref.
+
+## Type Declaration
+
+### target?
+
+```ts
+optional target?: TTarget;
+```
+
+The element to listen on. Defaults to document.
+
+## Type Parameters
+
+### TOptions
+
+`TOptions`
+
+### TTarget
+
+`TTarget`
diff --git a/docs/reference/classes/AppHotkeysStore.md b/docs/reference/classes/AppHotkeysStore.md
new file mode 100644
index 00000000..98eacd41
--- /dev/null
+++ b/docs/reference/classes/AppHotkeysStore.md
@@ -0,0 +1,303 @@
+---
+id: AppHotkeysStore
+title: AppHotkeysStore
+---
+
+Defined in: [app-hotkeys.ts:349](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L349)
+
+Holds declared app hotkeys and the user's custom hotkeys. Framework
+providers create one and expose it through hooks; devtools read it from
+`HotkeyManager.appHotkeys`.
+
+## Example
+
+```ts
+const store = new AppHotkeysStore(appHotkeys, {
+ customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'),
+ onCustomHotkeysChange: (custom) =>
+ localStorage.setItem('hotkeys', JSON.stringify(custom)),
+})
+store.setCustomHotkey('doc.save', 'Mod+Shift+S')
+```
+
+## Type Parameters
+
+### T
+
+`T` *extends* [`AppHotkeys`](../type-aliases/AppHotkeys.md) = [`AppHotkeys`](../type-aliases/AppHotkeys.md)
+
+## Constructors
+
+### Constructor
+
+```ts
+new AppHotkeysStore(appHotkeys, options?): AppHotkeysStore;
+```
+
+Defined in: [app-hotkeys.ts:354](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L354)
+
+#### Parameters
+
+##### appHotkeys
+
+`T`
+
+##### options?
+
+[`AppHotkeysStoreOptions`](../interfaces/AppHotkeysStoreOptions.md)\<`T`\> = `{}`
+
+#### Returns
+
+`AppHotkeysStore`\<`T`\>
+
+## Properties
+
+### store
+
+```ts
+readonly store: Store>;
+```
+
+Defined in: [app-hotkeys.ts:350](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L350)
+
+## Accessors
+
+### appHotkeys
+
+#### Get Signature
+
+```ts
+get appHotkeys(): T;
+```
+
+Defined in: [app-hotkeys.ts:372](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L372)
+
+Effective app hotkeys.
+
+##### Returns
+
+`T`
+
+***
+
+### customHotkeys
+
+#### Get Signature
+
+```ts
+get customHotkeys(): CustomHotkeys;
+```
+
+Defined in: [app-hotkeys.ts:377](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L377)
+
+The user's hotkeys that differ from the defaults.
+
+##### Returns
+
+[`CustomHotkeys`](../type-aliases/CustomHotkeys.md)\<`T`\>
+
+## Methods
+
+### findConflicts()
+
+```ts
+findConflicts(id, hotkey): AppHotkey[];
+```
+
+Defined in: [app-hotkeys.ts:413](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L413)
+
+Other app hotkeys that would collide if `id` used `hotkey`. App hotkeys
+collide when their contexts match or either has no context, and their
+hotkeys are equal or one sequence starts with the other.
+
+#### Parameters
+
+##### id
+
+keyof `T` & `string`
+
+##### hotkey
+
+[`Hotkey`](../type-aliases/Hotkey.md)
+
+#### Returns
+
+[`AppHotkey`](../interfaces/AppHotkey.md)\<`string`,
+ \| [`HotkeyChord`](../type-aliases/HotkeyChord.md)
+ \| [`HotkeySequence`](../type-aliases/HotkeySequence.md)
+ \| `null`\>[]
+
+***
+
+### get()
+
+```ts
+get(id):
+ | AppHotkey
+ | undefined;
+```
+
+Defined in: [app-hotkeys.ts:404](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L404)
+
+Effective app hotkey for an id, or `undefined` when it is not declared.
+
+#### Parameters
+
+##### id
+
+`string`
+
+#### Returns
+
+ \| [`AppHotkey`](../interfaces/AppHotkey.md)\<`string`,
+ \| [`HotkeyChord`](../type-aliases/HotkeyChord.md)
+ \| [`HotkeySequence`](../type-aliases/HotkeySequence.md)
+ \| `null`\>
+ \| `undefined`
+
+***
+
+### getList()
+
+```ts
+getList(registrations): AppHotkeyListItem[];
+```
+
+Defined in: [app-hotkeys.ts:480](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L480)
+
+Joins app hotkeys with live registrations, in declaration order.
+Adapters call this with the manager's registration views.
+
+#### Parameters
+
+##### registrations
+
+`Iterable`\<[`HotkeyRegistrationView`](../type-aliases/HotkeyRegistrationView.md)\>
+
+#### Returns
+
+[`AppHotkeyListItem`](../interfaces/AppHotkeyListItem.md)\<`T`\[keyof `T`\]\>[]
+
+***
+
+### resetCustomHotkey()
+
+```ts
+resetCustomHotkey(id): void;
+```
+
+Defined in: [app-hotkeys.ts:457](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L457)
+
+Restores the default hotkey for an app hotkey.
+
+#### Parameters
+
+##### id
+
+keyof `T` & `string`
+
+#### Returns
+
+`void`
+
+***
+
+### resetCustomHotkeys()
+
+```ts
+resetCustomHotkeys(): void;
+```
+
+Defined in: [app-hotkeys.ts:464](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L464)
+
+Restores every default hotkey.
+
+#### Returns
+
+`void`
+
+***
+
+### setAppHotkeys()
+
+```ts
+setAppHotkeys(appHotkeys): void;
+```
+
+Defined in: [app-hotkeys.ts:390](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L390)
+
+Replaces the declarations, for example after hot module replacement.
+Custom hotkeys for ids that still exist are kept.
+
+#### Parameters
+
+##### appHotkeys
+
+`T`
+
+#### Returns
+
+`void`
+
+***
+
+### setCustomHotkey()
+
+```ts
+setCustomHotkey(id, hotkey): AppHotkey[];
+```
+
+Defined in: [app-hotkeys.ts:432](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L432)
+
+Sets the user's hotkey for an app hotkey. `null` unbinds it. A hotkey
+equal to the default removes the customization.
+
+#### Parameters
+
+##### id
+
+keyof `T` & `string`
+
+##### hotkey
+
+[`Hotkey`](../type-aliases/Hotkey.md) \| `null`
+
+#### Returns
+
+[`AppHotkey`](../interfaces/AppHotkey.md)\<`string`,
+ \| [`HotkeyChord`](../type-aliases/HotkeyChord.md)
+ \| [`HotkeySequence`](../type-aliases/HotkeySequence.md)
+ \| `null`\>[]
+
+Conflicting app hotkeys. When any exist, nothing changes.
+
+***
+
+### setOptions()
+
+```ts
+setOptions(options): void;
+```
+
+Defined in: [app-hotkeys.ts:382](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/app-hotkeys.ts#L382)
+
+Updates callbacks and other options without resetting custom hotkeys.
+
+#### Parameters
+
+##### options
+
+`Omit`\<[`AppHotkeysStoreOptions`](../interfaces/AppHotkeysStoreOptions.md)\<`T`\>, `"customHotkeys"`\>
+
+#### Returns
+
+`void`
diff --git a/docs/reference/classes/HotkeyManager.md b/docs/reference/classes/HotkeyManager.md
index 23a15316..b05beabb 100644
--- a/docs/reference/classes/HotkeyManager.md
+++ b/docs/reference/classes/HotkeyManager.md
@@ -3,38 +3,63 @@ id: HotkeyManager
title: HotkeyManager
---
-Defined in: [hotkey-manager.ts:191](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L191)
+Defined in: [hotkey-manager.ts:311](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L311)
-Singleton manager for hotkey registrations.
+Singleton manager for hotkey registrations, including sequences.
-This class provides a centralized way to register and manage keyboard hotkeys.
-It uses a single event listener for efficiency, regardless of how many hotkeys
-are registered.
+This class provides a centralized way to register and manage keyboard
+hotkeys. It attaches one keydown and one keyup listener per target,
+regardless of how many hotkeys are registered.
+
+Sequences (Vim-style hotkeys such as `['G', 'G']`) match when every step is
+pressed within the timeout. Modifier-only events (Shift, Control, Alt, or
+Meta alone) neither advance nor reset a sequence, so chained modifier chords
+such as `['Shift+R', 'Shift+T']` work.
## Example
```ts
const manager = HotkeyManager.getInstance()
-const unregister = manager.register('Mod+S', (event, context) => {
+const save = manager.register('Mod+S', (event, context) => {
console.log('Save triggered!')
})
+const top = manager.register(['G', 'G'], () => scrollToTop(), { timeout: 500 })
// Later, to unregister:
-unregister()
+save.unregister()
+top.unregister()
```
## Properties
+### appHotkeys
+
+```ts
+readonly appHotkeys: Store<
+ | AppHotkeysStore>>>
+| null>;
+```
+
+Defined in: [hotkey-manager.ts:340](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L340)
+
+The app hotkeys store published by a framework provider, so tools such as
+devtools can list declared hotkeys that are not registered.
+
+***
+
### registrations
```ts
readonly registrations: Store