TanStack Hotkeys: React Keyboard Shortcuts – Production-ready einsetzen

TanStack Hotkeys Production-Guide: Barrierefreie Tastenkürzel mit Scope-Isolation, Screen-Reader-Support und konfliktfreien Keymaps.

Huifer
Huifer
18. September 20268 min read
Auch verfügbar auf:English · 中文

Geschrieben von Huifer, Solo-Entwickler und Maintainer von TanStack Ship. In 2025 habe ich Tastaturnavigation für ein Compliance-Admin-Panel implementiert – ein SaaS-Produkt im Umfeld einer Behörde, das WCAG 2.1 AA einhalten musste. Das Schwierigste waren nicht die Tastenkürzel selbst, sondern sie zum Laufen zu bringen, ohne Screen Reader zu brechen, ohne Konflikte mit Browser-Shortcuts und ohne gegen das Focus-Management anzukämpfen, das Reacts Komponentenmodell mit sich bringt. Ich habe auf die harte Tour gelernt, dass onKeyDown auf einem <div> keine Tastaturnavigation ist – es ist ein Shortcut, der 20 % der Nutzer ausschließt. Dieser Guide fasst zusammen, was ich gelernt habe.

Verifizierte Quellen: TanStack Hotkeys on GitHub · WAI-ARIA Keyboard Navigation · MDN Keyboard Event Handling Letzte Aktualisierung: 2026-09-18 · Changelog

TL;DR: Tastenkürzel in React brauchen drei Dinge: Richtige ARIA-Widget-Patterns für Barrierefreiheit, Scope-Isolation damit Shortcuts nicht global feuern, wenn man in einem Eingabefeld ist, und Browser-Shortcut-Konfliktmanagement. TanStack Hotkeys (@tanstack/react-hotkeys, Alpha) stellt die Binding-Schicht bereit; die Barrierefreiheits-Patterns sind Ihre Verantwortung.


Die Barrierefreiheits-Grundlage

Bevor Sie ein Tastenkürzel schreiben, verinnerlichen Sie folgendes: Tastaturnavigation ist keine Shortcut-Funktion. Sie ist eine Barrierefreiheits-Anforderung für Nutzer, die keine Maus verwenden können. Dazu gehören Nutzer mit motorischen Einschränkungen, Power-User, die ausschließlich per Tastatur arbeiten, und die 20 % der Entwickler, die Vim-Keybindings in ihrer IDE nutzen.

Ein <div> mit einem onKeyDown-Handler erfüllt die technische Anforderung von WCAG 2.1. Er scheitert an der operativen Anforderung von WCAG 2.1, wenn das Focus-Management nicht an assistive Technologien kommuniziert wird.

Die drei Regeln für barrierefreie Tastaturnavigation:

  1. Jedes tastaturnavigierbare Widget muss in der Tab-Reihenfolge sein – entweder über tabindex="0" oder über ein role-Attribut
  2. Der Focus-Zustand muss sichtbar sein – niemals outline: none verwenden, ohne eine alternative sichtbare Fokus-Angabe bereitzustellen
  3. Screen Reader müssen den Widget-Zustand kennen – nutzen Sie aria-pressed, aria-expanded, aria-selected oder aria-activedescendant

TanStack Hotkeys: Die Binding-Schicht

TanStack Hotkeys stellt die Event-Bindung bereit. Es kümmert sich um keydown/keyup-Binding, Scope-Management und die Modifier-Key-Behandlung, die bei Raw Event Listeners mühsam wird.

bash
npm install @tanstack/react-hotkeys
tsx
import { HotkeysProvider, useHotkeys } from '@tanstack/react-hotkeys'

function App() {
  return (
    <HotkeysProvider>
      <Editor />
    </HotkeysProvider>
  )
}

function Editor() {
  useHotkeys(
    'command+k', // macOS
    (event) => {
      event.preventDefault()
      openCommandPalette()
    },
    { scopes: ['editor'] }
  )

  useHotkeys(
    'command+b',
    (event) => {
      event.preventDefault()
      toggleBold()
    },
    { scopes: ['editor'] }
  )

  return <div>Editor content</div>
}

useHotkeys bindet Keyboard-Events innerhalb eines Scopes. Scopes sind entscheidend für die Scope-Isolation: Wenn der Nutzer in einem Texteingabefeld ist, darf der command+k-Shortcut des Editors nicht feuern.


Scope-Isolation: Das Pattern, das Shortcuts in echten Apps funktionieren lässt

Der häufigste Bug bei Tastenkürzel-Implementierungen: Shortcuts feuern in Texteingabefeldern und zerstören die Fähigkeit des Nutzers zu tippen.

TanStack Hotkeys löst dieses Problem mit Scopes. Wenn ein Scope „aktiv" ist, feuern seine Shortcuts. Wenn ein Scope „inaktiv" ist, werden seine Shortcuts unterdrückt.

tsx
import { HotkeysProvider, useHotkeys, useHotkeysScope } from '@tanstack/react-hotkeys'

function Editor() {
  useHotkeys(
    'command+k',
    () => openCommandPalette(),
    { scopes: ['editor'] }
  )

  return (
    <div>
      <RichTextEditor
        onFocus={() => useHotkeysScope('editor')}
        onBlur={() => useHotkeysScope('global')}
      />
    </div>
  )
}

Der useHotkeysScope-Hook aktiviert oder deaktiviert einen Scope basierend auf dem Focus. Wenn der Nutzer im Rich-Text-Editor ist, ist der editor-Scope aktiv und Shortcuts feuern. Wenn der Nutzer zu einem Texteingabefeld außerhalb des Editors tabbt, deaktiviert sich der Scope und Shortcuts werden unterdrückt – mit Ausnahme derer, die im global-Scope registriert sind.

tsx
// Globale Shortcuts feuern in jedem Scope
useHotkeys(
  'command+n',
  () => createNewDocument(),
  { scopes: ['global'], enableOnFormTags: true }
)

enableOnFormTags: true sorgt dafür, dass der Shortcut auch innerhalb von Formular-Eingabefeldern feuert – nützlich für globale Shortcuts wie „Neues Dokument erstellen", die von überall funktionieren sollen.


Roving Tabindex: Das ARIA-Pattern für komplexe Widgets

Bei komplexen Widgets wie einer Toolbar oder einem Menü ist die Tab-Reihenfolge nicht linear. Das Widget erhält den Focus als Ganzes (ein Tab-Stop), und Pfeiltasten navigieren innerhalb des Widgets. Das ist das Roving-Tabindex-Pattern.

TanStack Hotkeys unterstützt dies durch Scope-Management:

tsx
function Toolbar() {
  const items = ['bold', 'italic', 'underline', 'link']
  const [activeIndex, setActiveIndex] = useState(-1)

  useHotkeys(
    'left',
    () => {
      setActiveIndex(i => Math.max(0, i - 1))
    },
    { scopes: ['toolbar'] }
  )

  useHotkeys(
    'right',
    () => {
      setActiveIndex(i => Math.min(items.length - 1, i + 1))
    },
    { scopes: ['toolbar'] }
  )

  useHotkeys(
    'enter',
    () => {
      if (activeIndex >= 0) {
        activateItem(items[activeIndex])
      }
    },
    { scopes: ['toolbar'] }
  )

  return (
    <div
      role="toolbar"
      tabIndex={0}
      onFocus={() => {
        setActiveIndex(-1) // Start mit keinem aktiven Item
        useHotkeysScope('toolbar')
      }}
      onBlur={() => useHotkeysScope('global')}
    >
      {items.map((item, i) => (
        <button
          key={item}
          aria-pressed={activeIndex === i}
          tabIndex={activeIndex === i ? 0 : -1}
          onClick={() => activateItem(item)}
        >
          {item}
        </button>
      ))}
    </div>
  )
}

Das Roving-Tabindex-Pattern: Nur das aktuell aktive Item hat tabIndex={0}. Alle anderen Items haben tabIndex={-1}. Wenn der Nutzer Tab drückt, ist nur das aktive Item in der Tab-Reihenfolge. Pfeiltasten bewegen activeIndex und lösen ein Re-Render aus – das vorher aktive Item bekommt tabIndex={-1} und das neue bekommt tabIndex={0}. Screen Reader wissen, welches Item aktiv ist, über aria-pressed.


Browser-Shortcut-Konflikte

Drei Browser-Shortcuts können nicht überschrieben werden:

  • Command/Ctrl+W – Tab schließen
  • Command/Ctrl+T – Neuer Tab
  • Command/Ctrl+R – Aktualisieren
  • Command/Ctrl+Shift+T – Geschlossenen Tab wiederherstellen
  • F5 / Ctrl+R – Aktualisieren

TanStack Hotkeys ruft event.preventDefault() für übereinstimmende Shortcuts auf. Bei den oben genannten Shortcuts wird preventDefault() vom Browser ignoriert. Wenn Ihre App diese Shortcuts behandeln muss, müssen Sie das auf App-Ebene tun – beachten Sie aber, dass das Browser-Verhalten Vorrang hat.

Der häufigste Konflikt, auf den ich in der Produktion gestoßen bin: Escape zum Schließen von Modals. Wenn der Nutzer eine Browser-Erweiterung hat, die Escape für ihre eigene UI abfängt, schließt sich Ihr Modal nicht. Behandeln Sie dies, indem Sie zusätzlich onKeyDown auf dem Modal-Element selbst abfangen – das Element-Level-Event feuert vor dem globalen Listener.


Production-Checkliste

  • Jedes Tastenkürzel hat eine barrierefreie Alternative – einen Button, Menüpunkt oder UI-Control, der dasselbe tut
  • Der Focus-Zustand ist sichtbar – nutzen Sie ein CSS :focus-visible-Outline, das auf jedem Hintergrund sichtbar ist
  • Shortcuts feuern nicht in Texteingabefeldern, außer es ist beabsichtigt – nutzen Sie Scope-Isolation
  • Komplexe Widgets nutzen Roving Tabindex – ARIA-Pattern für Toolbar, Menü, Tree, Listbox
  • Shortcuts sind dokumentiert – binden Sie ein Keyboard-Shortcuts-Panel ein (drücken Sie ? zum Öffnen)
  • Screen-Reader-Tests durchgeführt – testen Sie die App mit VoiceOver (macOS) oder NVDA (Windows) vor dem Launch
  • Keine Browser-Shortcut-Konflikte – testen Sie, dass Escape Modals schließt, ohne sich ausschließlich auf preventDefault() zu verlassen

Weiterführende Lektüre: Tanstack Form Complete Guide · Tanstack Pacer Complete Guide 2026 · Tanstack Virtual Complete Guide 2026

Wann Sie auf TanStack Hotkeys warten sollten

TanStack Hotkeys befindet sich in der Alpha-Phase. Die API-Oberfläche ist nicht stabil. Bevor Sie es in einem ausgelieferten Produkt verwenden:

  • Pinnen Sie eine exakte Version in package.json
  • Beobachten Sie TanStack Hotkeys auf GitHub auf Breaking Changes
  • Die useHotkeysScope-API kann sich vor 1.0 noch ändern

Für Produktions-Apps, die heute Stabilität brauchen, nutzen Sie @tanstack/react-hotkeys auf eigenes Risiko, oder verwenden Sie die stabilere react-hotkeys-Library mit manuellem Scope-Management.


TanStack Hotkeys ist als optionale Dependency in TanStack Ship verfügbar. Weitere Informationen zum vollständigen Feature-Set und zur Einordnung der Keyboard-Barrierefreiheit ins WCAG-2.1-Compliance-Muster.