Amazon Developer

as

Settings
Sign out
Notifications
Alexa
Amazon Appstore
Ring
AWS
Documentation
Support
Contact Us
My Cases
Get Started
Design and Develop
Publish
Reference
Support

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:

Copied to clipboard.


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:

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):

Copied to clipboard.


<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:

Copied to clipboard.


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

Copied to clipboard.


<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:

Copied to clipboard.


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

Copied to clipboard.


// hasTVPreferredFocus works only on initial mount
<Pressable hasTVPreferredFocus>
  <Text>I get focus first</Text>
</Pressable>

For dynamic focus control, use FocusManager:

Copied to clipboard.


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

Copied to clipboard.


// 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:

Copied to clipboard.


<Pressable
  enableSynchronousFocusEvents
  onFocus={() => setFocused(true)}
  onBlur={() => setFocused(false)}>
  <Text>Stable focus</Text>
</Pressable>

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


Last updated: Jun 29, 2026