useHeldKeys
The keys someone is holding down right now, shared by every subscriber through a single set of window listeners.
[]pnpm dlx shadcn@latest add https://hextaui.com/r/use-held-keys.jsonAdds the hook and anything it depends on to your project.
Copy and paste the following code into your project.
hooks/use-held-keys.ts Update the import paths to match your project setup.
Use it for anything that reacts to keys being held rather than pressed: keycaps that press down, shortcut hints that appear while you hold ⌘, or a modifier that switches a tool, like Alt to duplicate while dragging.
Every component that calls the hook shares one store. The first subscriber adds passive keydown and keyup listeners to window, and the last one to unsubscribe removes them. A page with fifty listening keycaps still has one pair of listeners.
The listeners only read events. They never call preventDefault, and they sit on window, after React's own handlers, so typing in a field is never delayed or changed.
- Letters and digits come from
event.code, the physical key, so holding Option+K on a Mac still reportskrather than˚. - Key repeat is ignored, and nothing re-renders while a key is held.
- macOS doesn't send
keyupfor other keys while ⌘ is down. When ⌘ is released, the store keeps only the modifiers still held, so letters can't get stuck. - Everything is released when the window loses focus or the tab is hidden. A shortcut that switches apps leaves nothing held.
Keycaps that press
<Kbd listen> is built on this hook. Each keycap presses down while its key is held.
Hold the modifiers to see the hint
- Pass
falseto stop listening. The hook then returns an empty set and adds no listeners, so it's cheap to call it unconditionally. - The set is replaced only when a key goes down or up, so its identity works as a memo or effect dependency.
- For shortcuts that fire an action, use
matchesHotkeyfromHotkeyin a keydown handler instead. Holding keys is for showing state, not for running commands. - On the server, and before hydration, the set is empty.
| Prop | Type | Default |
|---|---|---|
enabledWhether to listen. When false, nothing is attached. | boolean | – |
| Returns | Description |
|---|---|
ReadonlySet<string> | The names of the keys held down right now. |
Kbd and KbdGroup through their listen prop.