useKeyHeld(key) returns a reactive reader that flips between true
and false as the key goes down and up — useful for game-style input,
canvas controls, or any UI driven by held state rather than key-press
events. Multiple subscribers share a single window-level tracker
(keydown/keyup/blur listeners on window) that auto-detaches once the
last subscriber unmounts. key follows KeyboardEvent.key
lowercased: 'a', ' ' for space, 'arrowup', 'shift', etc. For a
non-reactive live set use keysHeld. Part
of pota/use/keyboard.
Keydown is ignored while focus is inside an editable element
(<input>, <textarea>, <select>, or contenteditable) so typing
doesn't trip held state — the reader stays false for keys pressed
while editing. Keyup is always honored so keys can't get stuck, and OS
key-repeat events don't flip the signal: only the actual press/release
transitions do.
| Argument | Type | Description |
|---|---|---|
key |
string |
Key to track, matched lowercased against event.key. |
Returns: () => boolean — a reader that is true while the key
is held.
Toggle a class while the Shift key is down — the reactive class function re-evaluates on each press and release.
import { render } from 'pota'
import { useKeyHeld } from 'pota/use/keyboard'
function App() {
const shift = useKeyHeld('shift')
return (
<>
<style>{`.shifted { background: gold; }`}</style>
<p class={() => (shift() ? 'shifted' : '')}>
hold <kbd>Shift</kbd> to highlight this text
</p>
</>
)
}
render(App)
Derive UI directly from held-key readers — no effects needed. Holding an arrow key keeps the offset reactive while it is down.
import { render, memo } from 'pota'
import { useKeyHeld } from 'pota/use/keyboard'
function App() {
const up = useKeyHeld('arrowup')
const down = useKeyHeld('arrowdown')
const y = memo(() => (up() ? -1 : 0) + (down() ? 1 : 0))
return (
<div>
<p>
direction:{' '}
{() => (y() === 0 ? 'idle' : y() < 0 ? 'up' : 'down')}
</p>
<p>hold the arrow keys (not while typing in a field)</p>
</div>
)
}
render(App)