Use useHotkeySequenceRecorder to record an ordered sequence of chords. Recording defaults to physical codes, such as ['[KeyG]', 'Alt+[KeyS]']. Use recordBy: 'key' for logical characters. Pass the saved array directly to useHotkeySequence.
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.
import { useState } from 'octane'
import { useHotkeySequence, useHotkeySequenceRecorder, formatForDisplay } from '@tanstack/octane-hotkeys'
import type { HotkeySequence } from '@tanstack/octane-hotkeys'
export function SequenceSettings() @{
const [binding, setBinding] = useState<HotkeySequence>(['G', 'G'])
const recorder = useHotkeySequenceRecorder({ onRecord: setBinding })
useHotkeySequence(binding, () => console.log('Go to top'))
<div>
<button type="button" onClick={recorder.startRecording}>Record sequence</button>
@if (recorder.isRecording) {
<div>
<p>{recorder.steps.map((step) => formatForDisplay(step)).join(' → ')}</p>
<button type="button" onClick={recorder.commitRecording}>Save</button>
<button type="button" onClick={recorder.cancelRecording}>Cancel</button>
</div>
}
</div>
}Press and release each chord, then press Enter or click Save. Cancellation leaves the saved binding unchanged.
| Property | Type | Meaning |
|---|---|---|
| isRecording | boolean | Whether a session is active. |
| steps | HotkeySequence | Chords captured in the current session. |
| recordedSequence | HotkeySequence | null | The last committed sequence. |
| startRecording | () => void | Start a new session. |
| stopRecording | () => void | Stop without calling onRecord or onCancel. |
| cancelRecording | () => void | Discard the session and call onCancel. |
| commitRecording | () => void | Commit current steps; do nothing if empty. |
Set hotkeySequenceRecorder defaults in HotkeysProvider. Options passed to an individual hook override provider defaults. For a plural hook, each definition's options override its common options. See the provider setup. Hook options refresh after each commit.
The default is true, so normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels. Set ignoreInputs: false to record in a focused input.
validate(sequence, { events, parsedSequence }) runs when committing and returns true, false, or a rejection message. detectConflicts checks single bindings and sequence prefixes. A rejected commit keeps the session active and the steps intact, so the user can edit them with Backspace.
The recorder supports the same detectConflicts, validate, and onReject options as the single-hotkey recorder. Use live registration IDs to exclude the binding being edited. Physical/logical overlap is based on the captured events, not a guessed keyboard layout.
| Input | Behavior |
|---|---|
| Valid chord | Append to steps and continue listening. |
| Unmodified Enter with default commit settings and nonempty steps | Commit and call onRecord. |
| Escape | Cancel and call onCancel. |
| Unmodified Backspace or Delete with nonempty steps | Remove the last step without committing. |
| Unmodified Backspace or Delete with empty steps | Stop and call only onClear. |
| Modifier-only press, repeat, or IME composition | Do not append a step. |
Recorded chords use portable Mod. Recording events, commit keys, and their releases are isolated from application hotkeys and sequences. Code mode rejects AltGraph character entry; key mode preserves the produced character without synthetic Control/Alt.
Octane subscribes with @tanstack/octane-store and destroys the recorder on unmount.
See the useHotkeySequenceRecorder example for editable sequence settings and the kitchen sink for manual commit, idle timeout, validation, and conflict feedback.