Understanding Focus on React Native TVOS (the easy way)

Sofia
3 min readDec 27, 2023

--

As a React Native developer using this framework for a few years, I initially assumed that things would be the same for TVOS as for Android TV, but it’s a whole new world — though perhaps not entirely. It is just a matter of time before things fall into place.

If you are reading this, you might be wondering why you can’t navigate to the button above the handy one you just coded. In the following picture, you can see an imaginary guide that illustrates how TVOS handles navigation. When you focus on the first Movies cell, clicking the Up Button won’t take you to the Series button, and hitting the Left Button won’t lead you anywhere since the Series button is neither on the x-axis nor the y-axis of the Movies Cell.

What is TVFocusGuideView?

As read on the react-native-tvos repo:

“TVFocusGuideView: This component provides support for Apple’s UIFocusGuide API, to help ensure that focusable controls can be navigated to, even if they are not directly in line with other controls.”

React Native TVOS has this functionality out of the box.

The solution is TVFocusGuideView. On the following code, we are wrapping the red section and the blue section inside a TVFocusGuideView.

 <View style={styles.container}>
<TVFocusGuideView autoFocus style={{backgroundColor: 'blue'}}>
<View style={styles.sectionTitleContainer}>
<View>
<Text style={styles.sectionTitle}>{selectedTitle}</Text>
</View>
<View style={styles.heroContainer}>
<TouchableOpacity style={styles.heroButton} onFocus={() => {}}>
<Text style={styles.heroButtonTitle}>Play Now</Text>
</TouchableOpacity>
</View>
</View>
</TVFocusGuideView>
<TVFocusGuideView autoFocus style={{backgroundColor: 'red'}}>
{data.map(renderCategory)}
</TVFocusGuideView>
</View>

Adding autoFocus will make the magic. As you can see, we can now move from the Movies Cell to the Play Now button even when it is not in the y-axis. It is also worth mentioning that TVFocusGuideView remembers the previous focused element, so you don’t have to worry about obtaining the previous reference.

The green section in the previous image is just a Drawer Navigator using a DrawerContentScrollView.

return (
<TVFocusGuideView style={styles.focusGuideContainer} autoFocus>
<DrawerContentScrollView contentContainerStyle={styles.drawerContent}>
{data.map(({category}) => (
<GenericButton
title={category}
titleStyle={titleStyle}
styleFocused={[categoryCommon, categoryFocused]}
style={[categoryCommon]}
onPress={handleNavigation}
/>
))}
</DrawerContentScrollView>
</TVFocusGuideView>
)

Be sure to give the proper style to the FocusGuideView:

  focusGuideContainer: {
height: '100%',
},

That’s it!

You can also manage the focus adding the prop destinations:

 <TVFocusGuideView destinations={[destination]} />

While this approach gets the job done, it doesn’t retain the last destination. In scenarios like moving from a FlatList to the ‘Play Now’ button, a more intricate method is required to manage the next reference if you intend to return to the FlatList. Leveraging the TVFocusGuideView with autoFocus can facilitate this task.

--

--