Here at Norigin we are building TV Streaming Experiences for a wide range of Large Screen devices. Recently, we decided to open source some of the code we use in our TV App framework, since we felt certain components could be beneficial across all Smart TV development projects.

Our first open source project addresses Smart TV Navigation with React.

When developing for Smart TVs (or Connected TVs), game consoles and set-top boxes, there is one special thing about them — User Input method. Normally it is remote control with the directional keys. For some TVs like LG there might be also a pointer input (Magic Remote), and for Apple TV there is a directional touch pad.

Spatial Navigation in action

This way of navigation is called Spatial Navigation (or Directional Navigation). To interact with the elements on the screen we have to move the focus (navigate to) the element and press a selection button (OK button, Enter button etc.) when the element is focused. There should be only one focused element on the screen. As developers we have to implement the logic for this type of navigation ourselves, as there is no default implementation. At least not on the Web platforms. The complexity of this feature is often underestimated and it can be quite challenging in certain scenarios. There is always a risk of introducing bugs like having more than one focused element on the screen or losing focus completely. It requires you to have a strict and robust state management system to keep track of what is focused on the screen and how to transfer the focus when transitioning between screens or modal elements. Luckily React has quite a lot of ways to organise and manage the state, so let’s have a look at a few patterns of how to implement spatial navigation.

Most common patterns

Distributed Navigation Logic. Perhaps the most straightforward pattern. Each component keeps the state of which child component is focused now, and also handles the key events to decide what to focus in response to those events. While this method might give a full control of navigation logic inside one component, it is not the most scalable solution. It requires to implement navigation logic for every component. This logic gets spread across the whole application and might take near 15% of the codebase. It also means that navigation logic needs to be tested for every single component and any improvements done in one component don’t benefit other use cases. Another issue with this approach is that all the components need to be aware of their parent components logic (e.g. expecting some prop from parent to indicate that it is focused now) as well as the children components structure. It becomes tricky when developing UI components in isolation from each other, when components might be frequently rearranged and ideally should not be aware of each other. When components are replaced or moved, it requires to also update the navigation logic in all relevant places. In the example below you can see a simplified implementation of distributed navigation of Home screen that renders multiple rows of gallery items. It handles vertical key events and switches rows, while every row handles horizontal key evens and switches between gallery items.

Simplified example of Distributed Navigation logic

Focus Maps is another common pattern when working with spatial navigation. Component might have a Focus Map, an object that is pre-calculated for each direction and contains the focus keys (focus IDs or indices) of the elements that needs to be focused in response to key press events. This allows to keep parent component clean from key handling, because it’s done in the children components. The parent component is still responsible for constructing of the Focus Map.

Simplified example of Focus Maps

This method is just a way to delegate key handling to children components. In some scenarios it makes it easier to also define special cases like focusing Side Menu. For example here GalleryRow decides when to call “left” focus map, and the logic for this is straightforward: it is called when the first item is focused in a Row and the left key press event occurs. The parent doesn’t need to handle this logic and can just focus Side Menu right away when onSetFocus is called with the Side Menu focus key param. However it’s still yet another variant of Distributed Navigation Logic and doesn’t scale well.

Helper Components. To organise the spatial navigation logic within the app we can use helpers such as FocusableComponent , HorizontalList , VerticalList , Grid to handle directional key events and manage the state of focused children components. This might help to encapsulate the navigation logic and to easily wrap any component inside FocusableComponent . These helpers can store the current focus key in a context, and every child FocusableComponent can subscribe to this context and see when it gets focused. This pattern is used in BBC T.A.L. and is the middle-ground between Distributed and Centralised Logic. The downside of this pattern is that you have to follow strict structure of your components tree and organise them in the rows, columns or grids as well as wrap every focusable component. In case when you have dynamic layout or using A/B testing in your app it might get hard to maintain since you need to update the structure of rows/columns whenever some components are moved around.

Doing it Smart

Since we are making apps for Smart TVs, our navigation system also has to be Smart, otherwise it wouldn’t work ¯\_(ツ)_/¯