npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

preact-spatial-navigation

v1.3.1

Published

A powerful Preact library for TV-style spatial navigation with LRUD algorithm, virtualized lists/grids, and smart TV support

Readme

Preact Spatial Navigation

A powerful, production-ready Preact library for TV-style spatial navigation using the LRUD algorithm from @bam.tech/lrud. Perfect for Smart TV apps, Set-top boxes, and any application requiring directional pad (D-pad) navigation.

Ported from react-tv-space-navigation to Preact with web optimizations.

✨ Features

  • 🎯 LRUD-based spatial navigation - Intelligent directional navigation
  • 📺 Smart TV ready - Works on Tizen, webOS, Android TV, and more
  • Virtualized lists & grids - Handle 10,000+ items smoothly
  • 🎨 Render props pattern - Full control over UI and focus states
  • 🔄 Auto-scrolling - Keeps focused items visible automatically
  • 📱 Device type aware - Keyboard, remote, and pointer support
  • 🎮 Flexible remote control - Easy key mapping configuration
  • Accessibility built-in - ARIA attributes out of the box
  • 📦 TypeScript first - Complete type definitions
  • 🌐 Old browser support - Chrome 35+ (ES2015)

📦 Installation

npm install preact-spatial-navigation
# or
yarn add preact-spatial-navigation

Peer dependencies:

npm install preact@^10.0.0

🚀 Quick Start

1. Configure Remote Control

import { configureRemoteControl, createKeyboardRemoteControl } from 'preact-spatial-navigation';

// For web development (keyboard arrows)
const { subscriber, unsubscriber } = createKeyboardRemoteControl();
configureRemoteControl({
  remoteControlSubscriber: subscriber,
  remoteControlUnsubscriber: unsubscriber,
});

2. Basic Navigation

import {
  SpatialNavigationRoot,
  SpatialNavigationNode,
  DefaultFocus,
} from 'preact-spatial-navigation';

function App() {
  return (
    <SpatialNavigationRoot isActive={true}>
      <DefaultFocus />
      
      <SpatialNavigationNode
        isFocusable
        onSelect={() => console.log('Selected!')}
      >
        {({ isFocused }) => (
          <button style={{
            backgroundColor: isFocused ? '#E91E63' : '#333',
            border: isFocused ? '3px solid white' : 'none',
          }}>
            Click Me {isFocused && '★'}
          </button>
        )}
      </SpatialNavigationNode>
    </SpatialNavigationRoot>
  );
}

📚 Core Components

SpatialNavigationRoot

The root provider that manages navigation state.

<SpatialNavigationRoot
  isActive={true}
  onDirectionHandledWithoutMovement={(direction) => {
    console.log('Reached border:', direction);
  }}
>
  {children}
</SpatialNavigationRoot>

SpatialNavigationNode

The core building block - can be focusable or a container.

// Focusable node
<SpatialNavigationNode
  isFocusable
  onFocus={() => console.log('Focused')}
  onSelect={() => console.log('Selected')}
>
  {({ isFocused, isActive }) => (
    <div>{isFocused ? 'FOCUSED' : 'Normal'}</div>
  )}
</SpatialNavigationNode>

// Container node
<SpatialNavigationNode orientation="horizontal">
  {({ isActive }) => (
    <div style={{ opacity: isActive ? 1 : 0.5 }}>
      <ChildNodes />
    </div>
  )}
</SpatialNavigationNode>

Props:

  • isFocusable: Whether node can receive focus
  • orientation: 'vertical' | 'horizontal'
  • onFocus, onBlur, onSelect, onLongSelect: Callbacks
  • onActive, onInactive: Container state changes
  • alignInGrid: Enable grid alignment
  • children: Render function or elements

SpatialNavigationView

Simple layout wrapper.

<SpatialNavigationView direction="horizontal">
  <Item1 />
  <Item2 />
  <Item3 />
</SpatialNavigationView>

SpatialNavigationScrollView

Auto-scrolling container.

<SpatialNavigationScrollView
  horizontal={false}
  offsetFromStart={100}
>
  <LongListOfItems />
</SpatialNavigationScrollView>

SpatialNavigationVirtualizedList

Efficiently render thousands of items.

<SpatialNavigationVirtualizedList
  data={items}
  itemSize={80}
  orientation="vertical"
  scrollBehavior="center"
  scrollDuration={200}
  renderItem={({ item, index }) => (
    <SpatialNavigationNode isFocusable>
      {({ isFocused }) => (
        <div style={{ height: '70px' }}>
          {item.title}
        </div>
      )}
    </SpatialNavigationNode>
  )}
/>

Scroll Behaviors:

  • stick-to-start: Focused item at top
  • stick-to-end: Focused item at bottom (recommended)
  • center: Item stays centered (hybrid mode)
  • jump-on-scroll: Page-by-page scrolling

SpatialNavigationVirtualizedGrid

Multi-column virtualized grid.

<SpatialNavigationVirtualizedGrid
  data={items}
  numberOfColumns={5}
  itemHeight={150}
  scrollBehavior="center"
  renderItem={({ item }) => (
    <SpatialNavigationNode isFocusable>
      {({ isFocused }) => (
        <div>{item.title}</div>
      )}
    </SpatialNavigationNode>
  )}
/>

🔧 Advanced Usage

Device Type Provider

import { SpatialNavigationDeviceTypeProvider } from 'preact-spatial-navigation';

<SpatialNavigationDeviceTypeProvider deviceType="tv">
  <App />
</SpatialNavigationDeviceTypeProvider>

Lock/Unlock Navigation

import { useLockSpatialNavigation } from 'preact-spatial-navigation';

function Modal() {
  const { lock, unlock } = useLockSpatialNavigation();
  
  useEffect(() => {
    lock();
    return () => unlock();
  }, []);
  
  return <div>Modal</div>;
}

Custom Remote Control

import { configureRemoteControl, Directions } from 'preact-spatial-navigation';

// For Samsung Tizen
configureRemoteControl({
  remoteControlSubscriber: (callback) => {
    const handler = (event) => {
      const keyMap = {
        37: Directions.LEFT,
        38: Directions.UP,
        39: Directions.RIGHT,
        40: Directions.DOWN,
        13: Directions.ENTER,
      };
      callback(keyMap[event.keyCode] || null);
    };
    window.addEventListener('keydown', handler);
    return handler;
  },
  remoteControlUnsubscriber: (handler) => {
    window.removeEventListener('keydown', handler);
  },
});

🎯 Platform Support

Tested Platforms

  • Samsung Tizen 2.4+
  • LG webOS 3.0+
  • Android TV
  • Desktop browsers (Chrome, Firefox, Safari, Edge)
  • Old browsers (Chrome 35+, ES2015)

Browser Support

  • Chrome 35+
  • Firefox 38+
  • Safari 9+
  • Edge 12+
  • Smart TV browsers (Samsung, LG, etc.)

📖 API Reference

Exports

// Components
export { SpatialNavigationRoot }
export { SpatialNavigationNode }
export { SpatialNavigationView }
export { SpatialNavigationFocusableView }
export { SpatialNavigationScrollView }
export { SpatialNavigationVirtualizedList }
export { SpatialNavigationVirtualizedGrid }
export { DefaultFocus }
export { SpatialNavigationDeviceTypeProvider }

// Hooks
export { useSpatialNavigator }
export { useLockSpatialNavigation }
export { useDeviceType }
export { useSpatialNavigatorFocusableAccessibilityProps }

// Configuration
export { configureRemoteControl, createKeyboardRemoteControl }
export { SpatialNavigation } // Namespace

// Utilities
export { Directions } // from @bam.tech/lrud
export { TV_REMOTE_KEYS }

// Types
export type { FocusableNodeState, NonFocusableNodeState }
export type { SpatialNavigationNodeRef }
export type { ScrollBehavior }
// ... and many more

🏗️ Building & Publishing

# Build library
npm run build

# Run demos locally
npm run dev

# Preview production build
npm run build:demo
npm run preview

📝 Migration from react-tv-space-navigation

This library maintains API compatibility with react-tv-space-navigation:

  • ✅ Same component names and props
  • ✅ Same render props pattern
  • ✅ Same LRUD-based navigation
  • ✅ Adapted for Preact and web platform
  • ✅ Added convenience features (keyboard remote control helper)

🤝 Contributing

Contributions welcome! The library is built with:

  • Preact - Fast 3KB React alternative
  • @bam.tech/lrud - Spatial navigation algorithm
  • TypeScript - Type safety
  • Vite - Fast build tool

📄 License

MIT

🙏 Credits


Made for TV. Built with ❤️