KeyHandler
The KeyHandler component is a focusable SceneGraph component that captures key events and forwards them to TypeScript.
To use KeyHandler, you wrap your content in the component, list the keys to capture in its keys prop, and handle each key event in the onKeyEvent callback (for example, to move focus or trigger an action).
Use cases
You can use KeyHandler to directly control key routing for a screen that has a few focusable areas. For more complex navigation patterns (grids, large lists, focus memory, and so on), you should use FocusBoundary instead.
Basic usage
The following example uses the KeyHandler component to capture remote control key presses and handle user input:
import { KeyHandler, EventHandlerKey } from "@roku-sdk/components";
const App = () => {
const onKey: EventHandlerKey = ({ key, press }) => {
if (press && key === "OK") {
console.log("Select pressed");
}
};
return (
<KeyHandler keys={["OK", "back"]} onKeyEvent={onKey} gainFocus>
{/* children */}
</KeyHandler>
);
};Props
The KeyHandler component accepts the following props to control key event handling behavior:
| Prop | Type | Description |
|---|---|---|
keys | RemoteKeyName[] | Specifies the key names that KeyHandler handles (for example, ["OK", "up", "down", "back"]). |
handleAllKeys | boolean | Claims all keys unconditionally. You cannot use it together with keys. |
bubbleUnhandledKeys | boolean | Propagates all keys that are not in the keys array to the parent (default). When false, KeyHandler consumes all of those keys without propagating them. |
onKeyEvent | EventHandlerKey | Specifies the callback, which has the signature ({ key, press }) => void. |
gainFocus | boolean | Sets SceneGraph focus on the node. |
How key handling works
The KeyHandler component extends Group with focusable=true. When it has SceneGraph focus, the following sequence of events handles each key:
- The BrightScript
onKeyEvent(key, press)function runs on the render thread. - If
handleAllKeysistrue,KeyHandlerforwards the key to TypeScript and returnstrue. - Otherwise (
handleAllKeysisfalse),KeyHandlerchecks whetherkeyis in thekeyslist. The possible results are as follows:- If the
keyis in the list,KeyHandlerforwards the key to TypeScript and returnstrue(the key is consumed). - If the
keyis not in the list andbubbleUnhandledKeysistrue,KeyHandlerreturnsfalse(the event propagates to the parent). - If the
keyis not in the list andbubbleUnhandledKeysisfalse,KeyHandlerreturnstrue(the key is consumed without propagating).
- If the
- TypeScript observes the
keyEventfield and calls youronKeyEventcallback.
Dynamic keys
The keys prop updates when you change its value. When you change it, the BrightScript side rebuilds its key map immediately. As a result, KeyHandler propagates the keys that you remove and captures the keys that you add. You can use this for modal states, mode cycling, or turning key groups on or off based on app state.
The following example changes the handled keys at run time:
const [activeKeys, setActiveKeys] = createSignal(["OK", "left", "right"]);
// Later: stop handling left/right, let them propagate
setActiveKeys(["OK"]);
<KeyHandler keys={activeKeys()} onKeyEvent={onKey} gainFocus>
{/* ... */}
</KeyHandler>Propagating key events with nested KeyHandlers
You can nest multiple KeyHandler components. In this case, the innermost focused handler receives the key event first. If it does not handle the key, the event propagates to the parent handler.
The following example nests three KeyHandler components:
<KeyHandler handleAllKeys onKeyEvent={onOuterKey}>
{/* Outer: catches everything that propagates up */}
<KeyHandler keys={["left", "right"]} onKeyEvent={onModifierKey}>
{/* Middle: handles left/right, propagates everything else */}
<KeyHandler keys={["OK", "up", "down"]} onKeyEvent={onInnerKey} gainFocus>
{/* Inner: has focus, handles OK/up/down */}
</KeyHandler>
</KeyHandler>
</KeyHandler>The following table shows how this example propagates different key events:
| Key press | Sequence |
|---|---|
| "OK" | The inner handler receives and handles the event; onInnerKey fires. |
| "left" | - The inner handler receives the event, but does not handle it. - The event propagates to the middle handler. - The middle handler receives and handles the event; onModifierKey fires. |
| "play" | - The inner handler receives the event, but does not handle it. - The event propagates to the middle handler. - The middle handler receives the event, but does not handle it. - The event propagates to the outer handler. - The outer handler receives and handles the event; onOuterKey fires. |
Using keyHandling with focus movement
You can use gainFocus, ref.focus(), or ref.blur() to move SceneGraph focus from your code. The following example moves focus between a header and a content area:
const App = () => {
let headerRef: any;
let contentRef: any;
const onKey: EventHandlerKey = ({ key, press }) => {
if (!press) return;
if (key === "down") contentRef.focus();
if (key === "up") headerRef.focus();
};
return (
<KeyHandler handleAllKeys onKeyEvent={onKey}>
<Header ref={headerRef} gainFocus />
<ContentArea ref={contentRef} />
</KeyHandler>
);
};Each focus move in this example makes a round trip (BrightScript to TypeScript and back to BrightScript). For a layout with many items or a complex grid, you should use
FocusBoundaryto move focus.
When to use KeyHandler
The following table lists the focus management approach to use for each navigation scenario:
| Scenario | Approach |
|---|---|
| Screen-level navigation (three to five sections) | KeyHandler + gainFocus / focus() |
| Simple back/exit handling | KeyHandler with keys={["back"]} |
| Navigation within a list of items | FocusableGroup or FocusBoundary |
| Complex grid, custom nav patterns | FocusBoundary and useFocusable |
| Mixed BrightScript and TypeScript component focus | FocusableGroup |
Example
You can review the KeyHandler example app for how to implement nested handlers, dynamic keys, and event propagation.
Related documentation
- KeyHandler: Includes the reference for the
KeyHandlercomponent. - Focus boundary: Describes the virtual focus system that routes focus and key handling in TypeScript.
- KeyHandler example: Describes the example app that demonstrates scoped key handling and propagation between nested handlers.
Updated about 6 hours ago