DEV Community

Neha Sharma for Amazon Appstore Developers

Posted on • Updated on

🧭 TV Navigation in React Native: A Guide to using TVFocusGuideView

While developing TV apps using react-native-tvos, you will notice differences in behaviour between platforms like tvOS and Android TV.

One such difference is in navigation.

In this blog, we will explore navigation issues and learn how to solve them.


TV apps are navigable using a remote control with a D-pad, which has four directional buttons: up, down, left, and right.

Both Android and tvOS have their native focus engines that determine the next element to focus on based on the current state and user input. However, their behaviours differ significantly.

In Android TV, there is a built-in focus engine that operates based on "proximity." This engine selects the next focusable element closest to the current focus, even if it doesn't align with the current element, which can lead to confusing and unexpected focus behavior 😤.

graph representation of the screen

Please read this to learn in detail how navigation works on both platforms.

In contrast, tvOS works on precision. It only changes the focus when there is an element directly next to the current one. Again, this can lead to confusing and unexpected focus behavior 😤.

In short, there are two main issues with the native focus engines of Android TV and tvOS at a higher level:

focus recovery and focus redirection

❓ What is the solution?

One way to solve this to use TVFocusGuideView 🎉🎉 .

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. As per the React Native TVOS

React Native TVOS has TVFocusGuideView functionality is out of the box.

💻 How to use TVFocusGuideView?

TVFocusGuideView is default integrated in the React Native TVOS. TVFocusGuideView has a few props that helps in managing the focus.

What will we be building?

To understand TVFocusGuideView and the use of its props, we will create an app. Please refer to the image below.

app screenshot

Lets have a look at some scenarios based on some typical user requirements. While implementing these requirements, we will learn usage of different props, and implementations of the TVFocusGuideView component.

props of tvfocusguideview

🥴 What if we don't use TVFocusGuideView

If we don't use TVFocusGuideView, there’s no control on the focus & navigation and the above requirements will work like this:

  1. Clicking the RIGHT button on the D-Pad will shift the focus to the first “touchable” element, typically within the grid.

  2. Clicking the LEFT button on the D-Pad will direct focus to the side navigation

  3. Pressing the DOWN button on the D-Pad will move focus outside the grid, and so forth.

app demo without tvfocusguideview

🥂 With TVFocusGuideView

app demo with tvfocusguideview


👩‍💻 Let's code

Let's start implementing all the requirements we discussed in "What we will be building?".

1. autoFocus

autoFocus is to programmatically managing the focus from one element to another.

Eg: on click of RIGHT Button, Focus should go to the hero image of main section from Sub Nav (Home button).

Without TVFocusGuideView's autoFocus the focus would skip the Hero Image and go to the the grid.

Why? Because focus works on “proximity algorithm”.

To move the focus from side nav to the hero image, we can use autoFocus from tvFocusGuideView.

Image description

  • Wrap the Hero component with tvFocusGuideView
  • Use autoFocus props on the top most wrapper
import React from 'react';
import {Text, TouchableOpacity} from 'react-native';

const Hero = () => {
  return (
    <TVFocusGuideView autoFocus>
      <Text >This is hero Image</Text>
      <TouchableOpacity>
        <Text>Play Now</Text>
      </TouchableOpacity>
    </TVFocusGuideView>
  );
};

export default Hero;
Enter fullscreen mode Exit fullscreen mode

b. Another example of autoFocus

Image description

<TVFocusGuideView autoFocus>
     <Hero />
     <Grid />
</TVFocusGuideView>
Enter fullscreen mode Exit fullscreen mode

2. trapFocus

trapFocus ensures the focus does not move or escape.

(Please, refer the below image) when LEFT button is pressed on the 1st and 5th box, the focus should remain in grid.

Image description

For this we need trapFocusLeft, if we don’t use trapFocusLeft, the focus will go to navigation from Grid.

  • Wrap the Grid component with tvFocusGuideView.
  • Use trapFocusLeft props with tvFocusGuideView.
import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';

const Grid = () => {
  return (
    <View>
      <TVFocusGuideView trapFocusLeft>
        <View>
          <TouchableOpacity>
            <Text>1</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>2</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>3</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>4</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>5</Text>
          </TouchableOpacity>
        </View>
        <View>
          <TouchableOpacity>
            <Text>6</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>7</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>8</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>9</Text>
          </TouchableOpacity>
          <TouchableOpacity>
            <Text>10</Text>
          </TouchableOpacity>
        </View>
      </TVFocusGuideView>

      <View>
        <TouchableOpacity>
          <Text>Outside Button</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};

export default Grid;
Enter fullscreen mode Exit fullscreen mode

b. One more example of trapFocus, on click of RIGHT button on D-pad, the focus should not go to the main section and stays at the side navigation.

We want user to move to main section only after reaching at the end of the navigation.

PS: this may not be a great CX but this is just to showcase the use of trapFocus.

To stop the focus moving to the main section we will use trapFocusLeft props of TVFocusGridView

Image description

In the provided code, we have wrapped our Nav component with TVFocusGuideView and added the props trapFocusRight .

Now , on click of RIGHT button on D-pad, the focus will not move to the main section. Instead, it will remain inside the Nav . By using DOWN button we can navigate to all items within the Nav

import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';

const Nav= () => {
  return (
    <TVFocusGuideView trapFocusRight>
      <View>
        <TouchableOpacity>
          <Text>Home</Text>
        </TouchableOpacity>
        <TouchableOpacity>
          <Text>About</Text>
        </TouchableOpacity>
        <TouchableOpacity>
          <Text>Live</Text>
        </TouchableOpacity>
        <TouchableOpacity>
          <Text>Settings</Text>
        </TouchableOpacity>
      </View>
    </TVFocusGuideView>
  );
};
export default Nav;
Enter fullscreen mode Exit fullscreen mode

3. destinations

To override default previous destinations, you can use destinations props.

Eg: On click of Left button , move focus from hero Image to “about” and not to “settings”.

Image description

In the below code, we have done the following:

  • Wrapped the Nav component with TVFocusGuideView
  • Utilised useRef, to obtain the reference of items.
  • Utilised destinations props to define the previous elements. -- - - Remember, since we are using refs, we need to reference them by about.current
  • Test time. Now, press back from hero image and you will observe that focus is going to About and not to the last focused element settings
import React, {useRef} from 'react';
import {View, Text, TouchableOpacity} from 'react-native';

const Nav= () => {
  const about = useRef();

  return (
    <TVFocusGuideView
      trapFocusRight
      destinations={[about.current]}>
      <View>
        <TouchableOpacity >
          <Text>Home</Text>
        </TouchableOpacity>
        <TouchableOpacity ref={about} style={styles.nav}>
          <Text>About</Text>
        </TouchableOpacity>
        <TouchableOpacity>
          <Text>Live</Text>
        </TouchableOpacity>
        <TouchableOpacity>
          <Text>Settings</Text>
        </TouchableOpacity>
      </View>
    </TVFocusGuideView>
  );
};

export default Nav;

Enter fullscreen mode Exit fullscreen mode

📚 Resources

PRs for TVFocusGuideView


⭐ Summary:

  1. Navigation Differences: There are differences in navigation behavior between tvOS and Android TV when developing TV apps with react-native-tvos.

  2. Focus Engines: Android TV uses a proximity-based focus engine and tvOS uses a precision-based focus engine, both leading to confusing behaviors.

  3. Solution: TVFocusGuideView as a solution for better focus management, supporting Apple’s UIFocusGuide API.

  4. props: TVFocusGuideView's props have autoFocus, trapFocus, destinations.

  5. Benefits: TVFocusGuideView provides more controlled and predictable navigation, improving user experience.

Happy Learning!!

If you like this blog, please share it with your friends and drop a comment. If you found anything that could be improved, please reach out to me on X and Linkedin.

Top comments (1)

Collapse
 
anishamalde profile image
Anisha Malde

Thankyou @hellonehha, this was such an easy to digest explanation of such a complicated topic!