Handling User Input
Users interact with Vega apps through touch (on multimodal devices) or a D-Pad remote control (on TV). React Native provides components that handle both input methods — onPress fires whether the user taps the screen or presses the select button on a remote.
Pressable components
The recommended way to handle user input is with Pressable. It works across all Vega profiles:
import React, {useState} from 'react';
import {Pressable, Text, StyleSheet, View} from 'react-native';
const PressableBasics = () => {
const [pressed, setPressed] = useState(false);
return (
<View style={styles.container}>
<Pressable
style={({pressed}) => [styles.button, pressed && styles.buttonPressed]}
onPress={() => console.log('Selected!')}>
<Text style={styles.text}>Press Me</Text>
</Pressable>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
padding: 20,
backgroundColor: '#2196F3',
borderRadius: 4,
},
buttonPressed: {
opacity: 0.7,
},
text: {
color: 'white',
textAlign: 'center',
},
});
export default PressableBasics;
Other touchable components:
- Button — A simple button rendered natively on all platforms.
- TouchableOpacity — Reduces opacity on press.
- TouchableWithoutFeedback — Handles press with no visual feedback.
All of these respond to both touch and D-Pad select.
Long press
Detect when a user presses and holds (touch) or long-presses the select button (D-Pad):
<Pressable
onPress={() => console.log('Pressed!')}
onLongPress={() => console.log('Long pressed!')}>
<Text>Press or hold</Text>
</Pressable>
TV Navigation (D-Pad)
On TV devices, there is no touchscreen. Users navigate using directional buttons (up, down, left, right) and a select button. The system moves focus between interactive elements based on a Cartesian distance algorithm.
Focus indicators
Every interactive element must show a clear visual change when focused. Use physical changes (border, scale) — not just color or opacity:
const FocusableCard = ({title, onSelect}) => {
const [focused, setFocused] = useState(false);
return (
<Pressable
style={[styles.card, focused && styles.cardFocused]}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
onPress={onSelect}>
<Text style={styles.cardText}>{title}</Text>
</Pressable>
);
};
const styles = StyleSheet.create({
card: {
padding: 16,
margin: 8,
backgroundColor: '#333',
borderWidth: 3,
borderColor: 'transparent',
},
cardFocused: {
borderColor: '#00BFFF',
transform: [{scale: 1.05}],
},
cardText: {
color: 'white',
fontSize: 16,
},
});
Focusable components
| Component | Default Focusable | Can Override with focusable prop |
|---|---|---|
Button |
Yes | No (always focusable) |
Pressable |
Yes | No (always focusable) |
TouchableOpacity |
Yes | No (always focusable) |
TouchableWithoutFeedback |
Yes | Yes (can disable) |
View |
No | Yes (can enable) |
Text |
No | Yes (can enable) |
Image |
No | Yes (can enable) |
TextInput |
Yes | Yes (can disable) |
Making non-interactive components focusable
<View
focusable={true}
onFocus={() => console.log('View focused')}
onBlur={() => console.log('View blurred')}>
<Text>This view can receive focus</Text>
</View>
Controlling focus direction
Override the default focus algorithm using nextFocus props:
import {findNodeHandle} from 'react-native';
import React, {useRef, useState, useEffect} from 'react';
const ref1 = useRef(null);
const ref2 = useRef(null);
const [handles, setHandles] = useState({ref1: null, ref2: null});
useEffect(() => {
setHandles({
ref1: findNodeHandle(ref1.current),
ref2: findNodeHandle(ref2.current),
});
}, []);
<Pressable ref={ref1} nextFocusDown={handles.ref2}>
<Text>Item 1</Text>
</Pressable>
<Pressable ref={ref2} nextFocusUp={handles.ref1}>
<Text>Item 2</Text>
</Pressable>
Available directional props: nextFocusUp, nextFocusDown, nextFocusLeft, nextFocusRight.
Setting initial focus
// hasTVPreferredFocus works only on initial mount
<Pressable hasTVPreferredFocus>
<Text>I get focus first</Text>
</Pressable>
For dynamic focus control, use FocusManager:
import {FocusManager} from '@amazon-devices/react-native-kepler';
import {findNodeHandle} from 'react-native';
const itemRef = useRef(null);
useEffect(() => {
// setTimeout needed due to known timing issue on first mount
setTimeout(() => {
const handle = findNodeHandle(itemRef.current);
if (handle) {
FocusManager.focus(handle);
}
}, 100);
}, []);
<Pressable ref={itemRef}>
<Text>Focused programmatically</Text>
</Pressable>
TVFocusGuideView
Advanced focus container for controlling focus behavior in complex layouts.
import {TVFocusGuideView} from '@amazon-devices/react-native-kepler';
| Prop | Type | Description |
|---|---|---|
autoFocus |
boolean |
First focusable child on first visit, last focused child on return |
destinations |
any[] |
Array of component refs to register as focus destinations |
trapFocusUp |
boolean |
Prevents focus from leaving upward |
trapFocusDown |
boolean |
Prevents focus from leaving downward |
trapFocusLeft |
boolean |
Prevents focus from leaving leftward |
trapFocusRight |
boolean |
Prevents focus from leaving rightward |
// Trap focus within a horizontal row
<TVFocusGuideView trapFocusLeft trapFocusRight>
<FlatList
horizontal
data={items}
renderItem={({item}) => (
<Pressable onPress={() => handleSelect(item)}>
<Text>{item.title}</Text>
</Pressable>
)}
/>
</TVFocusGuideView>
FocusManager API
Imperative focus control via TurboModule:
import {FocusManager} from '@amazon-devices/react-native-kepler';
| Method | Description |
|---|---|
FocusManager.focus(tag) |
Set focus on component |
FocusManager.blur(tag) |
Remove focus from component |
FocusManager.setNextFocus(fromTag, toTag, direction) |
Override next focus target (direction: 'up' | 'down' | 'left' | 'right') |
FocusManager.clearNextFocus(fromTag) |
Clear all focus overrides for component |
FocusManager.setFocusRoot(tag, isRoot) |
When true, prevents focus from leaving the component and its children |
FocusManager.getFocused() |
Returns node handle of currently focused component |
Handling rapid key presses
If focus behaves unexpectedly during rapid D-Pad presses, use the enableSynchronousFocusEvents prop:
<Pressable
enableSynchronousFocusEvents
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}>
<Text>Stable focus</Text>
</Pressable>
onFocus/onBlur completes. Keep callbacks lightweight. Do not call FocusManager.focus() inside these callbacks when this prop is enabled.Troubleshooting
| Issue | Solution |
|---|---|
FocusManager.focus() fails on mount |
Add setTimeout delay in useEffect |
| Focus resets during rapid key presses | Enable enableSynchronousFocusEvents on child components |
| Focus lost after re-render with zIndex change | Enable enableSynchronousFocusEvents |
TVFocusGuideView TypeScript error |
Add {/* @ts-expect-error */} before the component |
Further reading
- Focus Manager API — Full API reference
- TVEventHandler — Listen for raw remote control key events
- Pressable — Recommended component for press handling
Last updated: Jun 29, 2026

