MCPcopy Index your code
hub / github.com/NoriginMedia/Norigin-Spatial-Navigation

github.com/NoriginMedia/Norigin-Spatial-Navigation @v2.3.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v2.3.0 ↗ · + Follow
97 symbols 206 edges 15 files 11 documented · 11% 1 cross-repo links updated 7d ago@noriginmedia/norigin-spatial-navigation-react-native-tvos@4.0.0 · 2026-07-01★ 4684 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Norigin Spatial Navigation

Norigin Spatial Navigation is an open-source library that enables navigating between focusable elements built with ReactJS based application software. To be used while developing applications that require key navigation (directional navigation) on Web-browser Apps and other Browser based Smart TVs and Connected TVs. Our goal is to make navigation on websites & apps easy, using React Javascript Framework and React Hooks. Navigation can be controlled by your keyboard (browsers) or Remote Controls (Smart TV or Connected TV). Software developers only need to initialise the service, add the Hook to components that are meant to be focusable and set the initial focus. The Spatial Navigation library will automatically determine which components to focus next while navigating with the directional keys. We keep the library light, simple, and with minimal third-party dependencies.

npm version

Illustrative Demo

Norigin Spatial Navigation can be used while working with Key Navigation and React JS. This library allows you to navigate across or focus on all navigable components while browsing. For example: hyperlinks, buttons, menu items or any interactible part of the User Interface according to the spatial location on the screen.

Example

Example Source

Supported Devices

The Norigin Spatial Navigation library is theoretically intended to work on any web-based platform such as Browsers and Smart TVs. For as long as the UI/UX is built with the React Framework, it works on the Samsung Tizen TVs, LG WebOS TVs, Hisense Vidaa TVs and a range of other Connected TVs. It can also be used in React Native apps on Android TV and Apple TV, however functionality will be limited. This library is actively used and continuously tested on many devices and updated periodically in the table below:

Platform Name
Web Browsers Chrome, Firefox, etc.
Smart TVs Samsung Tizen, LG WebOS, Hisense
Other Connected TV devices Browser Based settop boxes with Chromium, Ekioh or Webkit browsers
AndroidTV, AppleTV Only React Native apps, limited functionality

Related Blogs

  1. Use & benefits of using the Norigin Spatial Navigation library on Smart TVs here.

Changelog

A list of changes for all the versions for the Norigin Spatial Navigation: CHANGELOG.md

Table of Contents

Installation

npm i @noriginmedia/norigin-spatial-navigation --save

Usage

Initialization

Init options

// Called once somewhere in the root of the app

import { init } from '@noriginmedia/norigin-spatial-navigation';

init({
  // options
});

New Distance Calculation Configuration

Starting from version 2.2.0, you can configure the method used for distance calculations between focusable components. This can be set during initialization using the distanceCalculationMethod option.

How to use | Available Options

  • edges: Calculates distances using the closest edges of the components.
  • center: Calculates distances using the center points of the components for size-agnostic comparisons. Ideal for non-uniform elements between siblings.
  • corners: Calculates distances using the corners of the components, between the nearest corners. This is the default value.
import { init } from '@noriginmedia/norigin-spatial-navigation';

init({
  // options
  distanceCalculationMethod: 'center', // or 'edges' or 'corners' (default)
});

Custom Distance Calculation Function

In addition to the predefined distance calculation methods, you can define your own custom distance calculation function. This will override the getSecondaryAxisDistance method.

You can pass your custom distance calculation function during initialization using the customDistanceCalculationFunction option. This function will override the built-in methods.

How to use | Available Options

The custom distance calculation function should follow the DistanceCalculationFunction type signature:

type DistanceCalculationFunction = (
  refCorners: Corners,
  siblingCorners: Corners,
  isVerticalDirection: boolean,
  distanceCalculationMethod: DistanceCalculationMethod
) => number;

Example

import { init } from '@noriginmedia/norigin-spatial-navigation';

// Define a custom distance calculation function
const myCustomDistanceCalculationFunction = (refCorners, siblingCorners, isVerticalDirection, distanceCalculationMethod) => {
  // Custom logic for distance calculation
  const { a: refA, b: refB } = refCorners;
  const { a: siblingA, b: siblingB } = siblingCorners;
  const coordinate = isVerticalDirection ? 'x' : 'y';

  const refCoordinateCenter = (refA[coordinate] + refB[coordinate]) / 2;
  const siblingCoordinateCenter = (siblingA[coordinate] + siblingB[coordinate]) / 2;

  return Math.abs(refCoordinateCenter - siblingCoordinateCenter);
};

// Initialize with custom distance calculation function
init({
  // options
  customDistanceCalculationFunction: myCustomDistanceCalculationFunction,
});

Making your component focusable

Most commonly you will have Leaf Focusable components. (See Tree Hierarchy) Leaf component is the one that doesn't have focusable children. ref is required to link the DOM element with the hook. (to measure its coordinates, size etc.)

import { useFocusable } from '@noriginmedia/norigin-spatial-navigation';

function Button() {
  const { ref, focused } = useFocusable();

  return (


    Press me


);
}

Wrapping Leaf components with a Focusable Container

Focusable Container is the one that has other focusable children. (i.e. a scrollable list) (See Tree Hierarchy) ref is required to link the DOM element with the hook. (to measure its coordinates, size etc.) FocusContext.Provider is required in order to provide all children components with the focusKey of the Container, which serves as a Parent Focus Key for them. This way your focusable children components can be deep in the DOM tree while still being able to know who is their Focusable Parent. Focusable Container cannot have focused state, but instead propagates focus down to appropriate Child component. You can nest multiple Focusable Containers. When focusing the top level Container, it will propagate focus down until it encounters the first Leaf component. I.e. if you set focus to the Page, the focus could propagate as following: Page -> ContentWrapper -> ContentList -> ListItem.

import { useFocusable, FocusContext } from '@noriginmedia/norigin-spatial-navigation';
import ListItem from './ListItem';

function ContentList() {
  const { ref, focusKey } = useFocusable();

  return (<FocusContext.Provider value={focusKey}>



      <ListItem />
      <ListItem />
      <ListItem />



  </FocusContext.Provider>);
}

Manually setting the focus

You can manually set the focus either to the current component (focusSelf), or to any other component providing its focusKey to setFocus. It is useful when you first open the page, or i.e. when your modal Popup gets mounted.

import React, { useEffect } from 'react';
import { useFocusable, FocusContext, setFocus } from '@noriginmedia/norigin-spatial-navigation';

function Popup() {
  const { ref, focusKey, focusSelf } = useFocusable();

  // Focusing self will focus the Popup, which will pass the focus down to the first Child (ButtonPrimary)
  // Alternatively you can manually focus any other component by its 'focusKey'
  useEffect(() => {
    focusSelf();

    // alternatively
    // setFocus('BUTTON_PRIMARY');
  }, [focusSelf]);

  return (<FocusContext.Provider value={focusKey}>



      <ButtonPrimary focusKey={'BUTTON_PRIMARY'} />
      <ButtonSecondary />



  </FocusContext.Provider>);
}

Tracking children components

Any Focusable Container can track whether it has any Child focused or not. This feature is disabled by default, but it can be controlled by the trackChildren flag passed to the useFocusable hook. When enabled, the hook will return a hasFocusedChild flag indicating when a Container component is having focused Child down in the focusable Tree. It is useful for example when you want to style a container differently based on whether it has focused Child or not.

import { useFocusable, FocusContext } from '@noriginmedia/norigin-spatial-navigation';
import MenuItem from './MenuItem';

function Menu() {
  const { ref, focusKey, hasFocusedChild } = useFocusable({trackChildren: true});

  return (<FocusContext.Provider value={focusKey}>



      <MenuItem />
      <MenuItem />
      <MenuItem />



  </FocusContext.Provider>);
}

Restricting focus to a certain component boundaries

Sometimes you don't want the focus to leave your component, for example when displaying a Popup, you don't want the focus to go to a component underneath the Popup. This can be enabled with isFocusBoundary flag passed to the useFocusable hook.

Additionally focusBoundaryDirections array can be provided to restrict focus movement only in specific directions. That might be useful when defining focus container for menu bar. Please note, that focusBoundaryDirections requires isFocusBoundary to be set to true.

import React, { useEffect } from 'react';
import { useFocusable, FocusContext } from '@noriginmedia/norigin-spatial-navigation';

function Popup() {
  const { ref, focusKey, focusSelf } = useFocusable({isFocusBoundary: true, focusBoundaryDirections: ['up', 'down']});

  useEffect(() => {
    focusSelf();
  }, [focusSelf]);

  return (<FocusContext.Provider value={focusKey}>



      <ButtonPrimary />
      <ButtonSecondary />



  </FocusContext.Provider>);
}

Using the library in React Native environment

In React Native environment the navigation between focusable (Touchable) components is happening under the hood by the native focusable engine. This library is NOT doing any coordinates measurements or navigation decisions in the native environment. But it can still be used to keep the currently focused element node reference and its focused state, which can be used to highlight components based on the focused or hasFocusedChild flags.

import { TouchableOpacity, Text } from 'react-native';
import { useFocusable } from '@noriginmedia/norigin-spatial-navigation';

function Button() {
  const { ref, focused, focusSelf } = useFocusable();

  return (<TouchableOpacity
    ref={ref}
    onFocus={focusSelf}
    style={focused ? styles.buttonFocused : styles.button}
  >
    <Text>Press me</Text>
  </TouchableOpacity>);
}

IMPORTANT TO NOTE: - Native mode needs to be enabled during initialization when using the library in a React Native environment - In order to "sync" the focus events coming from the native focus engine to the hook the onFocus callback needs to be linked with the focusSelf method. This way the hook will know that the component became focused and will set the focused flag accordingly.

API

Top Level exports

SpatialNavigation (advanced)

Top level API class singleton that provides access to the Navigation Service and its methods. Not recommended for beginners as it provides access to all internal methods of the core navigation service.

init

Init options

debug: boolean (default: false)

Enables console debugging.

visualDebug: boolean (default: false)

Enables visual debugging (all layouts, reference points and siblings reference points are printed on canvases).

nativeMode: boolean (default: false)

Enables Native mode. It will disable certain web-only functionality: - adding window key listeners - measuring DOM layout - onFocus and onBlur callbacks don't return coordinates, but still return node ref which can be used to measure layout if needed - coordinates calculations when navigating (smartNavigate in SpatialNavigation.ts) - navigateByDirection - focus propagation down the Tree - last focused child feature - preferred focus key feature

In other words, in the Native mode this library DOES NOT set the native focus anywhere via the native focus engine. Native mode should be only used to keep the Tree of focusable components and to set the focused and hasFocusedChild flags to enable styling for focused components and containers. In Native mode you can only call focusSelf in the component that gets native focus (via onFocus callback of the Touchable components) to flag it as focused. Manual setFocus method is blocked because it will not propagate to the native focus engine and won't do anything.

throttle: integer (default: 0)

Enables throttling of the key event listener.

throttleKeypresses: boolean (default: false)

Works only in combination with throttle > 0. By default, throttle only throttles key d

Extension points exported contracts — how you extend this code

UseFocusableConfig (Interface)
(no doc)
src/useFocusable.ts
NodeLayout (Interface)
(no doc)
src/VisualDebugger.ts
FocusableComponentLayout (Interface)
(no doc)
src/SpatialNavigation.ts
MenuItemBoxProps (Interface)
(no doc)
src/App.tsx
UseFocusableResult (Interface)
(no doc)
src/useFocusable.ts
FocusableComponent (Interface)
(no doc)
src/SpatialNavigation.ts
MenuWrapperProps (Interface)
(no doc)
src/App.tsx
FocusableComponentUpdatePayload (Interface)
(no doc)
src/SpatialNavigation.ts

Core symbols most depended-on inside this repo

getCurrentFocusKey
called by 40
src/SpatialNavigation.ts
log
called by 33
src/SpatialNavigation.ts
navigateByDirection
called by 23
src/SpatialNavigation.ts
setFocus
called by 14
src/SpatialNavigation.ts
addFocusable
called by 7
src/SpatialNavigation.ts
updateLayout
called by 6
src/SpatialNavigation.ts
drawPoint
called by 4
src/VisualDebugger.ts
getNodeLayoutByFocusKey
called by 4
src/SpatialNavigation.ts

Shape

Method 53
Function 20
Interface 19
Class 4
Enum 1

Languages

TypeScript100%

Modules by API surface

src/SpatialNavigation.ts59 symbols
src/App.tsx15 symbols
src/VisualDebugger.ts9 symbols
src/measureLayout.ts5 symbols
src/useFocusable.ts3 symbols
src/__tests__/domNodes.ts3 symbols
src/useFocusContext.ts1 symbols
src/__tests__/measureLayout.test.ts1 symbols
src/WritingDirection.ts1 symbols

Used by 1 indexed graphs manifest dependencies, hub-wide

For agents

$ claude mcp add Norigin-Spatial-Navigation \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page