Skip to content

Shortcut Recorder Field

Records a keyboard chord by physical key position, and refuses the ones the browser owns.

DOM + CSS0 deps1 file · 8 KB TSX

Prominimalbrutalist
Preview

Props

Require a modifier

The source is part of Pro

$27/month or $179 once. See Pro

No runtime dependencies
"use client";

import { useCallback, useEffect, useState, useSyncExternalStore } from "react";

export type Chord = {
  ctrl: boolean;
  alt: boolean;
  shift: boolean;
  meta: boolean;
  /** KeyboardEvent.code — a PHYSICAL key, not the character it produced. */
  code: string;
};

const MOD_CODES = new Set([
  "ControlLeft",
  "ControlRight",
  "AltLeft",
  "AltRight",

  // ---------------------------------------------------------------
  // Full source available with a viberdy Pro subscription.
  // https://viberdy.dev/pricing
  // ---------------------------------------------------------------
}
Details

About this component

Any app with a shortcuts panel needs this field, and almost every implementation gets the same thing wrong: it stores the character the key produced rather than the key itself. KeyboardEvent.key is layout-dependent, so binding Alt+K on macOS records a degree sign, and the same physical key on an AZERTY keyboard records a different letter entirely. KeyboardEvent.code is the physical position, which is what a shortcut actually means. The second omission is more damaging. A handful of chords belong to the browser or the operating system, and preventDefault cannot hold them — the event often never reaches the page. Capturing Cmd+W and displaying it as bound hands the user a shortcut that closes their tab on every press, so those chords are refused with a stated reason instead. Recording listens in the capture phase and stops propagation, which keeps the browser find bar closed and the app own hotkeys quiet while a chord is being chosen. Modifier keys are shown as they are held rather than ending the capture, and the platform-specific glyphs are resolved through useSyncExternalStore because reading navigator during render is a hydration mismatch waiting to happen.

Curator’s note

The reserved-chord list is the part people leave out. A recorder that happily accepts Cmd+W has not captured a shortcut, it has built a trap.

Pairs well with

Pro