TVFocusGuide
TVFocusGuide helps you to write intuitive TV apps. It supports autofocus
that helps in finding/recovering the focus as well as remembering the focus on multiple visits. It also supports trapping
and focus redirection
that allows you to customize the focus behavior in your app.
This component makes sure that focusable controls can be navigated to, even if they are not directly in line with other controls. An example is provided in RNTester
that shows two different ways of using this component.
Prop | Value | Description |
---|---|---|
destinations | any[]? | Array of Component s to register as destinations of the FocusGuideView |
autoFocus | boolean? | If true, TVFocusGuide will automatically manage focus for you. It will redirect the focus to the first focusable child on the first visit. It also remembers the last focused child and redirects the focus to it on the subsequent visits. destinations prop takes precedence over this prop when used together. |
trapFocus* (Up, Down, Left, Right) | Prevents focus escaping from the container for the given directions. |
The following animation shows navigating controls when using TVFocusGuideView
.
More information on the focus handling improvements above can be found in this article.
Example
import React from 'react';
import {
Dimensions,
View,
StyleSheet,
TouchableHighlight,
Text
} from 'react-native';
import {
TVFocusGuideView
} from '@amazon-devices/react-native-kepler';
const screenHeight = Dimensions.get('window').height;
const scale = screenHeight / 1080;
const width = 200 * scale;
const height = 120 * scale;
const theme = {
LinkColor: '#0984ffff',
LabelColor: '#ffffffff',
TertiarySystemFillColor: '#3085C3',
BackgroundColor: '#0c0700ff'
};
const Button = React.forwardRef((props: $FlowFixMeProps, ref) => {
const [focused, setFocused] = React.useState(false);
return (
<TouchableHighlight
onPress={() => {
if (props.onPress) {
props.onPress();
}
console.log(`${props.label} in press`);
}}
onFocus={() => {
if (props.onFocus) {
props.onFocus();
}
console.log(`${props.label} in focus`);
setFocused(true);
}}
onBlur={() => {
if (props.onBlur) {
props.onBlur();
}
console.log(`${props.label} in blur`);
setFocused(false);
}}
style={focused ? styles.buttonStyleFocused : styles.buttonStyle}
ref={ref}>
<Text style={[{ color: theme.LinkColor }, styles.buttonText]}>
{props.label}
</Text>
</TouchableHighlight>
);
});
const ThemedView = (props: $FlowFixMeProps) => {
return (
<View style={[styles.buttonStyle, props.style]}>
<Text style={[{ color: theme.LabelColor }, styles.buttonText]}>
{props.label}
</Text>
</View>
);
};
const TVFocusGuideExample = () => {
const [destination, setDestination] = React.useState(null);
const [destinationText, setDestinationText] = React.useState('');
const destinations = destination?.current ? [destination?.current] : [];
const buttonTopRight = React.useRef(null);
const buttonBottomLeft = React.useRef(null);
const rightButtonInFocusViewContainer = React.useRef(null);
const containerDestinations = rightButtonInFocusViewContainer?.current
? [rightButtonInFocusViewContainer?.current]
: [];
const _setDestination = (o: Object, text: string) => {
setDestination(o);
setDestinationText(text);
};
return (
<View
style={[styles.container, { backgroundColor: theme.BackgroundColor }]}>
<View style={styles.rowContainer}>
<Button onPress={() => {}} label="Left Top" />
<Button
onPress={() => {}}
onFocus={() => _setDestination(buttonBottomLeft, 'bottom left')}
ref={buttonTopRight}
label="Right Top"
/>
<ThemedView label={`Focus guide points to ${destinationText}`} />
{/* @ts-expect-error */}
<TVFocusGuideView
style={styles.containerFocusGuide}
destinations={containerDestinations}>
<Button onPress={() => {}} label="Wrapped button 1" />
<Button onPress={() => {}} label="Wrapped button 2" />
<Button
ref={rightButtonInFocusViewContainer}
onPress={() => {}}
label="Wrapped button 3"
/>
</TVFocusGuideView>
</View>
<View style={styles.rowContainer}>
<Button
onPress={() => {}}
onFocus={() => _setDestination(buttonTopRight, 'top right')}
ref={buttonBottomLeft}
label="Left Bottom"
/>
<TVFocusGuideView
style={[
{ backgroundColor: theme.TertiarySystemFillColor },
styles.focusGuide
]}
destinations={destinations}>
<Text style={[{ color: theme.LabelColor }, styles.buttonText]}>
Focus guide
</Text>
</TVFocusGuideView>
<ThemedView label="" />
<ThemedView
style={{
width: width * 3
}}
label="Blue focus guide container above always points to button 3
if navigating from outside"
/>
</View>
</View>
);
};
const marginSize = 20 * scale;
const styles = StyleSheet.create({
container: {
marginTop: -marginSize,
height: screenHeight
},
rowContainer: {
flexDirection: 'row',
padding: 100 * scale
},
buttonText: {
fontSize: 30 * scale
},
buttonStyle: {
width,
height,
marginLeft: marginSize,
marginRight: marginSize,
marginTop: marginSize,
marginBottom: marginSize
},
buttonStyleFocused: {
opacity: 0.5,
borderColor: 'white',
borderWidth: 4,
width,
height,
marginLeft: marginSize,
marginRight: marginSize,
marginTop: marginSize,
marginBottom: marginSize
},
focusGuide: {
width,
height,
marginLeft: marginSize,
marginRight: marginSize,
marginTop: marginSize,
marginBottom: marginSize
},
containerFocusGuide: {
backgroundColor: 'transparent',
borderColor: 'blue',
borderWidth: 2,
flexDirection: 'row'
}
});
export default TVFocusGuideExample;
Reference
Props
autoFocus
If true, TVFocusGuide
will automatically manage focus for you. It will redirect the focus to the first focusable child on the first visit. It also remembers the last focused child and redirects the focus to it on the subsequent visits. destinations
prop takes precedence over this prop when used
Type | Required |
---|---|
boolean | No |
trapFocus* (Up, Down, Left, Right)
Prevents focus escaping from the container for the given directions.
Type | Required |
---|---|
boolean | No |
destinations
Array of Components to register as destinations of the FocusGuideView.
Type | Required |
---|---|
array of references | No |
Methods
setDestinations
setDestinations([ref1, ref2, ...]);
Array of Components to register as destinations of the FocusGuideView.
Type | Required |
---|---|
array of references | No |
Known issues
setDestinations
callback fails to update if component is not mounted. Either usedestinations
prop instead or add a delay using setTimeout before callingsetDestinations
- TVFocusGuide fails with
Property 'children' does not exist on type
when imported in .tsx with strict typechecking. Apps may add{/* @ts-expect-error */}
or{/* @ts-ignore */}
before component use to avoid the error.
const TVApp = () => {
return (
<View>
{/* @ts-expect-error */}
<TVFocusGuideView>
<Text>Hello World</Text>
</TVFocusGuideView>
</View>
);
};
Additional Resources
For information on additonal libraries, see Supported Third-Party Libraries and Services.
Last updated: Sep 30, 2025