TanStack
Guides

Sequence Recording Guide

Use injectHotkeySequenceRecorder 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.

Record and display a sequence

This example uses a Save button so plain Enter can be recorded as a step:

ts
import { Component } from '@angular/core'
import { formatForDisplay, injectHotkeySequenceRecorder } from '@tanstack/angular-hotkeys'

@Component({
  standalone: true,
  template: `
    <button (click)="recorder.startRecording()">Record sequence</button>
    <p>{{ label() }}</p>
    @if (recorder.isRecording()) {
      <button (click)="recorder.commitRecording()">Save</button>
      <button (click)="recorder.cancelRecording()">Cancel</button>
    }
  `,
})
export class SequenceRecorderComponent {
  readonly recorder = injectHotkeySequenceRecorder({
    commitKeys: 'none',
    onRecord: (sequence) => console.log('Register or persist:', sequence),
    onReject: ({ message }) => console.log(message),
  })
  readonly label = () =>
    (this.recorder.isRecording() ? this.recorder.steps() : this.recorder.recordedSequence() ?? [])
      .map((step) => formatForDisplay(step)).join(' → ')
}

State and controls

The recorder exposes isRecording, steps, and recordedSequence as signal 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 provideHotkeys({ 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. The application owns reset, persistence, and any binding being edited; clearing never calls onRecord([]).