useFocusable

Registers a component instance with the nearest FocusBoundary

import { useFocusable } from "@roku-sdk/navigation";
useFocusable(options: UseFocusableOptions): UseFocusableResult

Description

Registers a component instance with the nearest FocusBoundary.

Must be called inside a component rendered within a RootFocusBoundary or FocusBoundary. Registration (and the ordering of focusables for navigation) follows render order.

Parameters

NameTypeDescription
optionsUseFocusableOptionsOptions passed to useFocusable. Extends FocusHandlers with an optional pre-created focusId identity.

Return values

The leaf's focus state and registration helpers. See UseFocusableResult.

Example

const { isFocused, ref } = useFocusable({
    focusId: props.focusId,
    onKey: {
        [RemoteKey.Select]: press => {
            if (press) props.onSelect?.();
        }
    }
} satisfies UseFocusableOptions);

return (
    <group ref={ref} translation={[0, props.y]}>
        <rectangle width={400} height={56} color={isFocused() ? "0x0055DDFF" : "0x222222FF"} />
        <label text={props.label} color={isFocused() ? "0xFFFFFFFF" : "0xBBBBBBFF"} translation={[16, 14]} />
    </group>
);

Types

UseFocusableResult

Return value of useFocusable.

NameTypeDescription
focus() => voidImperatively move virtual focus to this component.
focusIdFocusIdThe stable identity object used by the focus system. Only needed when you let useFocusable auto-create the identity and need to pass it elsewhere programmatically. When you pre-create the identity via options.focusId, you already have it — focusId just mirrors it back.
hasFootprint() => booleanReactive accessor; true when this item is the remembered focus target but the boundary is inactive (SG focus is elsewhere). See below.
hasReturnFootprint() => booleanSee below.
isFocused() => booleanReactive accessor; use to drive visual focus state.
ref(el: unknown) => voidRef callback to attach to the root JSX element. Always defined — captures the DomNode for RootFocusBoundary focus-path computation (no-op when the root boundary has no setFocusPath).

UseFocusableResult.hasFootprint

Reactive accessor; true when this item is the remembered focus target but the boundary is inactive (SG focus is elsewhere). Use to render a dimmed focus indicator so the user knows where focus will land on return.

Evaluated against this boundary alone, so every inactive boundary marks its own remembered child — including regions focus will not return to, and regions with rememberLastFocus={false} that reopen at initialFocus. To mark only items focus will actually land on, use hasReturnFootprint.

UseFocusableResult.hasReturnFootprint

Reactive accessor; true when this item is where focus will land if its boundary becomes active again — the boundary is inactive, sits on the return path up to the nearest active ancestor, and this item is that boundary's return target (honouring rememberLastFocus / initialFocus; a root keeps its focused child, because focusActive does not reset). Unlike hasFootprint, an off-path nested region never marks an item.

Every dormant sibling region under the nearest active ancestor marks one item — the one focus lands on if the user moves into that region — so N sibling regions with focus in one of them show N - 1 footprints. Exactly one item is marked in the tree only when the root itself is inactive. For a marker around the whole region, use useFocusActions().hasReturnFootprint.

UseFocusableOptions

Options passed to useFocusable. Extends FocusHandlers with an optional pre-created focusId identity.

NameTypeDescription
focusId?FocusIdA pre-created stable object to use as this component's virtual focus identity. See below.

Extends FocusHandlers.

UseFocusableOptions.focusId

A pre-created stable object to use as this component's virtual focus identity. Use this when you need to pass the identity to FocusBoundary's initialFocus prop.

If omitted, useFocusable creates a new {} object automatically.

FocusHandlers

Handler interface that a focusable component can provide when calling useFocusable. All fields are optional.

NameTypeDescription
canReceiveFocus?() => booleanIf provided, the boundary will only move virtual focus here when this returns true. Defaults to always focusable.
onFocusGained?() => voidCalled when this component gains virtual focus.
onFocusLost?() => voidCalled when this component loses virtual focus.
onKey?KeyHandlerMapDeclarative key handler map. See below.

FocusHandlers.onKey

Declarative key handler map. Each key in the record is a remote key name that this component handles. The boundary derives handledKeys from Object.keys(onKey) and evaluates any when gates reactively.

Dispatch: when a key event arrives and the key exists in onKey, the handler is called directly. If the entry has a when gate that returns false at dispatch time, the handler is skipped and the key is treated as unhandled.


What’s Next

Did this page help you?