| name | multi-tv-builder-complete |
| displayName | Multi-TV Builder Complete |
| description | Complete knowledge base for building production-ready cross-platform TV applications with React Native - supporting Android TV, Apple TV, Fire TV (Fire OS & Vega OS), and Web TV platforms with a single codebase |
| keywords | tv, react-native, android-tv, apple-tv, fire-tv, vega, tvos, web-tv, video, streaming, spatial-navigation, monorepo, expo, migration, typescript |
| author | Giovanni Laquidara |
Multi-TV Builder - Complete Knowledge Base
Build production-ready cross-platform TV applications with React Native, supporting Android TV, Apple TV, Fire TV (Fire OS & Vega OS), and Web TV platforms with a single codebase.
Quick Reference
- Sample Repo: https://github.com/AmazonAppDev/react-native-multi-tv-app-sample
- Slash Commands:
/multi-tv-help,/multi-tv-setup,/multi-tv-build,/multi-tv-platform - Skills:
multi-tv-builder,apple-tv-troubleshooter,tmdb-integration - Author: Giovanni Laquidara
Core Capabilities
- Cross-platform TV support with single codebase for 5+ platforms
- Monorepo architecture with Yarn workspaces for apps and shared packages
- Spatial navigation optimized for TV remote controls and D-pad input
- Video playback with custom controls and remote integration
- Platform-specific optimizations using automatic file resolution
- Shared UI library with reusable TV-optimized components
- TypeScript-first with strict type safety across all packages
Supported Platforms
| Platform | Technology | Output |
|---|---|---|
| Android TV | react-native-tvos | APK/AAB |
| Apple TV (tvOS) | react-native-tvos + Expo | IPA |
| Fire TV (Fire OS) | react-native-tvos | APK |
| Fire TV (Vega OS) | Amazon Vega SDK | Optimized VPKG |
| Web TV | React Native Web | HTML/JS Bundle |
Product Overview
React Native Multi-TV App Sample
A production-ready TV application template for building cross-platform TV apps with a single React Native codebase.
Core Features
Video Playback
- Integrated video player with react-native-video (universal) or W3C Media APIs (Vega)
- Custom overlay controls with play/pause, seek bar, and time display
- Remote control integration (play/pause, seek ±10s, exit)
- Auto-hide controls after 5s of inactivity
- Buffering indicators during loading
Spatial Navigation
- React TV Space Navigation for focus management
- D-pad/arrow key navigation optimized for TV remotes
- Platform-specific remote control managers
- Focus restoration when returning from player
Content Discovery
- Dynamic content loading from external catalog API
- Grid layouts optimized for TV screens
- Detail screens with rich metadata (genre, rating, duration, year)
- Dynamic hero banner based on focused content
- Trending flags and content ratings
Navigation
- Drawer navigation with left-side menu
- Stack navigation for detail screens
- Platform-specific navigation optimizations
- Deep linking support (optional)
Responsive Design
- Adaptive layouts for different TV screen sizes
- Responsive scaling with useScale hook
- Platform-specific layout adjustments
- Safe zone handling for TV overscan
App Variants
- expo-multi-tv - Universal app supporting Android TV, Apple TV, Fire TV (Fire OS), and Web
- vega - Fire TV Vega OS optimized build using Amazon Vega SDK
Key Use Cases
- Streaming video applications
- Content discovery and browsing
- TV-optimized user interfaces
- Cross-platform TV development reference
Architecture & Structure
Monorepo Organization
project-root/
├── apps/ # Application packages
│ ├── expo-multi-tv/ # Universal TV app
│ └── vega/ # Fire TV Vega optimized
├── packages/ # Shared packages
│ └── shared-ui/ # Shared components
├── package.json # Workspace configuration
├── tsconfig.base.json # Shared TypeScript config
├── babel.config.js # Root babel configuration
└── yarn.lock # Dependency lock file
Apps Directory
apps/expo-multi-tv/
Universal TV application built with Expo SDK.
Key Files:
App.tsx- Main entry pointapp.json- Expo configurationpackage.json- App-specific dependenciestsconfig.json- TypeScript config extending basemetro.config.js- Metro bundler configurationios/- iOS/tvOS native codeandroid/- Android TV native code
Platforms Supported:
- Android TV
- Apple TV (tvOS)
- Fire TV (Fire OS)
- Web (LG WebOS, Samsung Tizen)
apps/vega/
Fire TV Vega OS optimized application using Amazon Vega SDK.
Key Files:
App.tsx- Main entry pointpackage.json- Vega-specific dependenciestsconfig.json- TypeScript configmetro.config.js- Metro bundler configurationandroid/- Fire TV Vega native code
Platform Supported:
- Fire TV (Vega OS)
Shared UI Package Structure
packages/shared-ui/src/
├── app/ # App-level configuration
│ └── remote-control/ # Platform-specific remote managers
│ ├── RemoteControlManager.android.ts
│ ├── RemoteControlManager.ios.ts
│ └── RemoteControlManager.kepler.ts
├── assets/ # Static assets (fonts, images, icons)
├── components/ # Reusable UI components
│ ├── player/ # Video player components
│ ├── CustomDrawerContent.tsx # Drawer menu
│ ├── FocusablePressable.tsx # TV-optimized pressable
│ ├── LoadingIndicator.tsx # Loading spinner
│ ├── MenuContext.tsx # Menu state management
│ └── PlatformLinearGradient.tsx # Cross-platform gradient
├── data/ # Data and API
│ └── moviesData.ts # Movie catalog API
├── hooks/ # Custom React hooks
│ └── useScale.ts # Responsive scaling
├── navigation/ # Navigation configuration
│ ├── AppNavigator.tsx # Main app navigator
│ ├── DrawerNavigator.tsx # Drawer navigation
│ ├── RootNavigator.tsx # Root wrapper
│ └── types.ts # Navigation types
├── screens/ # Screen components
│ ├── DetailsScreen.tsx # Content detail
│ ├── ExploreScreen.tsx # Content exploration
│ ├── HomeScreen.tsx # Home/landing
│ ├── PlayerScreen.tsx # Video player
│ ├── PlayerScreen.vega.tsx # Vega-specific player
│ ├── SettingsScreen.tsx # Settings
│ └── TVScreen.tsx # TV content
├── theme/ # Design system
│ ├── colors.ts # Color palette
│ ├── spacing.ts # Spacing scale
│ ├── typography.ts # Font styles
│ ├── safeZones.ts # TV safe zones
│ └── index.ts # Theme exports
├── utils/ # Utility functions
└── index.ts # Package exports
Platform-Specific File Resolution
Metro bundler automatically resolves platform files:
.kepler.ts/tsx- Fire TV Vega OS (highest priority for Vega builds).android.ts/tsx- Android TV & Fire TV Fire OS.ios.ts/tsx- Apple TV (tvOS).vega.tsx- Vega-specific screens.web.ts/tsx- Web platforms (LG WebOS, Samsung Tizen).ts/tsx- Default/shared implementation (lowest priority)
Example:
RemoteControlManager.android.ts → Android TV
RemoteControlManager.ios.ts → Apple TV
RemoteControlManager.kepler.ts → Fire TV Vega
Key Architectural Patterns
Shared UI Library Pattern
- All reusable components live in
@project/shared-uipackage - Apps import from shared package, never local files
- Platform-specific implementations use file extensions
- Single source of truth for UI components and screens
TV-First Design
- 10-foot UI: Larger touch targets, high-contrast colors, readable typography
- Safe zones: Content positioned within TV safe areas (see theme/safeZones.ts)
- Focus management: Clear visual focus indicators for all interactive elements
- Remote control: D-pad navigation with platform-specific key mappings
Technology Stack
Build System
- Yarn Workspaces v4+: Monorepo management
- Metro Bundler: Module bundling with platform-specific resolution
- TypeScript v5.3+: Strict mode enabled across all packages
Core Technologies
- React Native: v0.74+ (react-native-tvos fork)
- Expo SDK: v51+ (for universal app)
- React: v18.2+
- TypeScript: v5.3+ with strict mode
Navigation & Focus
- React Navigation v6: Drawer and stack navigators
- React TV Space Navigation: Spatial navigation for TV remotes
- Platform Remote Managers: Android (react-native-keyevent), iOS (TVEventHandler), Vega (Kepler SDK)
Video Playback
- react-native-video v6+: Universal app video player
- W3C Media APIs: Native video for Vega OS
- Custom overlay controls with spatial navigation
Platform-Specific SDKs
- Amazon Vega SDK: Fire TV Vega OS builds
- @amazon-devices packages: Kepler SDK components
- react-native-tvos: TV platform support (Android TV, Apple TV, Fire TV FOS)
Development Tools
- ESLint: Code quality with TypeScript and React plugins
- Prettier: Code formatting (120 char width, single quotes)
- Husky: Git hooks for pre-commit checks
- Commitlint: Conventional commit enforcement
- Jest: Unit testing
Implementation Patterns
Workspace Configuration
Root package.json:
{
"name": "tv-app-project",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"packageManager": "yarn@4.5.0",
"scripts": {
"dev": "yarn workspace @project/expo-multi-tv start",
"dev:android": "yarn workspace @project/expo-multi-tv android",
"dev:ios": "yarn workspace @project/expo-multi-tv ios",
"dev:web": "yarn workspace @project/expo-multi-tv web",
"dev:vega": "yarn workspace @project/vega start",
"build:vega": "yarn workspace @project/vega run build",
"test:all": "yarn workspaces foreach -pt run test",
"lint:all": "yarn workspaces foreach -pt run lint",
"clean:all": "yarn workspaces foreach run clean"
}
}
Theme System
Centralized design tokens in packages/shared-ui/src/theme/:
colors.ts:
export const colors = {
primary: '#E50914', // Netflix-style red
background: '#141414', // Dark background
card: '#2F2F2F', // Card background
text: '#FFFFFF', // Primary text
textSecondary: '#B3B3B3' // Secondary text
};
spacing.ts:
export const spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48
};
safeZones.ts:
export const safeZones = {
horizontal: 48, // Left/right margins for TV overscan
vertical: 27 // Top/bottom margins
};
Responsive Scaling
useScale hook:
import { Dimensions } from 'react-native';
const BASE_WIDTH = 1920;
const BASE_HEIGHT = 1080;
export const useScale = () => {
const { width, height } = Dimensions.get('window');
const scaleWidth = width / BASE_WIDTH;
const scaleHeight = height / BASE_HEIGHT;
const scale = Math.min(scaleWidth, scaleHeight);
return {
scale: (size: number) => size * scale,
width,
height
};
};
Remote Control Manager Interface
All platform managers implement this interface:
export interface RemoteControlManagerInterface {
initialize(): void;
cleanup(): void;
}
Android/Fire TV FOS (RemoteControlManager.android.ts):
import ReactNativeKeyEvent from 'react-native-keyevent';
export class RemoteControlManager implements RemoteControlManagerInterface {
initialize() {
ReactNativeKeyEvent.onKeyDownListener((keyEvent) => {
// Handle Android TV remote keys
});
}
cleanup() {
ReactNativeKeyEvent.removeKeyDownListener();
}
}
Apple TV (RemoteControlManager.ios.ts):
import { TVEventHandler } from 'react-native';
export class RemoteControlManager implements RemoteControlManagerInterface {
private tvEventHandler: TVEventHandler | null = null;
initialize() {
this.tvEventHandler = new TVEventHandler();
this.tvEventHandler.enable(this, (component, evt) => {
// Handle tvOS remote events
});
}
cleanup() {
this.tvEventHandler?.disable();
}
}
Fire TV Vega (RemoteControlManager.kepler.ts):
import { TVEventHandler } from '@amazon-devices/react-native-kepler';
export class RemoteControlManager implements RemoteControlManagerInterface {
private tvEventHandler: TVEventHandler | null = null;
initialize() {
this.tvEventHandler = new TVEventHandler();
this.tvEventHandler.enable(this, (component, evt) => {
// Handle Vega remote events
});
}
cleanup() {
this.tvEventHandler?.disable();
}
}
Spatial Navigation Setup
Integration with React TV Space Navigation:
import { SpatialNavigation } from 'react-tv-space-navigation';
// Initialize in App.tsx
SpatialNavigation.init({
debug: __DEV__,
visualDebug: __DEV__
});
// Use in components
import { useFocusable } from 'react-tv-space-navigation';
const MyComponent = () => {
const { ref, focused } = useFocusable();
return (
<View ref={ref} style={focused ? styles.focused : styles.default}>
{/* Content */}
</View>
);
};
Video Player Implementation
Universal App (expo-multi-tv):
import Video from 'react-native-video';
<Video
source={{ uri: videoUrl }}
style={styles.video}
controls={Platform.OS === 'ios'} // Native controls on iOS
resizeMode="contain"
onLoad={handleLoad}
onProgress={handleProgress}
ref={videoRef}
/>
Vega App:
// Use W3C Media APIs through Kepler SDK
import { VideoView } from '@amazon-devices/react-native-kepler';
<VideoView
source={{ uri: videoUrl }}
style={styles.video}
onLoad={handleLoad}
ref={videoRef}
/>
Dynamic Content Loading
Catalog API Integration:
// packages/shared-ui/src/data/moviesData.ts
export interface Movie {
id: string;
title: string;
genre: string;
rating: number;
contentRating: string;
year: number;
duration: number;
videoUrl: string;
imageUrl: string;
trending: boolean;
}
export const fetchMovies = async (): Promise<Movie[]> => {
const response = await fetch('https://api.example.com/catalog.json');
const data = await response.json();
return transformCatalogData(data);
};
Common Commands
Development
# Install all dependencies
yarn install
# Start Metro bundler for expo-multi-tv
yarn dev
# Run on specific platforms
yarn dev:android # Android TV & Fire TV FOS
yarn dev:ios # Apple TV
yarn dev:web # Web TV platforms
# Start Metro bundler for vega
yarn dev:vega
Building
Expo Multi-TV App:
# Build for all platforms
cd apps/expo-multi-tv
# Android TV / Fire TV (Fire OS)
npx expo build:android
# or: npx expo run:android
# Apple TV (tvOS)
npx expo build:ios
# or: npx expo run:ios
# Web TV platforms
npx expo export --platform web
Fire TV Vega App:
# From project root
yarn build:vega # Build release VPKG
yarn build:vega:debug # Build debug VPKG
# From apps/vega directory
cd apps/vega
# Build with npm (default from Vega CLI)
npm run build:app # Builds for all architectures
# Build with Kepler CLI (more control)
kepler build --arch armv7 --buildType release # Fire TV Stick
kepler build --arch aarch64 --buildType release # M-series Mac virtual devices
kepler build --arch x86_64 --buildType release # Intel virtual devices
# Clean build
rm -rf build && npm run build:app
Testing & Linting
# Run tests across all workspaces
yarn test:all
# Lint all workspaces
yarn lint:all
# Type check
yarn typecheck
# Format code
yarn format
Vega CLI Commands
Project Management:
# List available project templates
kepler project list-templates
# Generate new Vega project
kepler project generate \
--template hello-world \
--name myapp \
--packageId com.company.myapp \
--outputDir myapp
# Verify Vega SDK installation
kepler --version
Building:
# Build for specific architecture and build type
kepler build --arch <armv7|aarch64|x86_64> --buildType <debug|release>
# Examples:
kepler build --arch armv7 --buildType release # Production Fire TV Stick build
kepler build --arch aarch64 --buildType debug # Debug build for M-series Mac
kepler build --arch x86_64 --buildType debug # Debug build for Intel virtual device
Device Management:
# List connected devices
kepler device list
# Install VPKG on device
kepler install --vpkg <path-to-vpkg-file>
# Run app on device
kepler run --packageId com.company.myapp
Getting Started
Quick Start (Recommended)
The fastest way to get started is to clone the official React Native Multi-TV App Sample repository.
Clone the Repository:
# Clone the official sample repository
git clone https://github.com/AmazonAppDev/react-native-multi-tv-app-sample.git
cd react-native-multi-tv-app-sample
# Install dependencies
yarn install
# Bootstrap the monorepo
yarn bootstrap
What's Included:
- ✅ Pre-configured monorepo with Yarn workspaces
- ✅ expo-multi-tv app - Universal app for Android TV, Apple TV, Fire TV (Fire OS), and Web
- ✅ vega app - Fire TV Vega OS optimized app
- ✅ shared-ui package - Complete component library
- ✅ All configuration files
Next Steps After Cloning:
- Review the codebase
- Set up for Expo TV - Follow Expo TV documentation
- Set up for Vega (optional) - Install Vega SDK
- Run on platforms
- Customize for your needs
Alternative: Build from Scratch
1. Initialize Project Structure
# Create project directory
mkdir my-tv-app && cd my-tv-app
# Initialize package.json
yarn init -y
# Create directory structure
mkdir -p apps packages/shared-ui
2. Configure Shared TypeScript
Create tsconfig.base.json:
{
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"lib": ["ES2021"],
"jsx": "react-native",
"strict": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
3. Set Up Shared UI Package
cd packages/shared-ui
yarn init -y
# Install dependencies
yarn add react react-native
yarn add -D typescript @types/react @types/react-native
# Create src directory structure
mkdir -p src/{components,screens,navigation,hooks,theme,app/remote-control,data,utils,assets}
4. Create Universal App (expo-multi-tv)
IMPORTANT: Follow the official Expo TV setup guide.
Option 1: Start with the basic TV template:
cd apps
npx create-expo-app@latest expo-multi-tv -e with-tv
Option 2: Start with the TV Router template (recommended):
cd apps
npx create-expo-app@latest expo-multi-tv -e with-router-tv
After creating the app:
- Link shared-ui package by editing
apps/expo-multi-tv/package.json - Install additional TV dependencies
- Configure Metro bundler for monorepo
5. Create Vega App (Optional - Fire TV Vega OS Only)
Prerequisites: Install Vega SDK
Required Dependencies (macOS):
# For Apple Silicon Macs (includes Rosetta 2)
[[ $(arch) == "arm64" ]] && softwareupdate --install-rosetta --agree-to-license; brew update && brew install binutils coreutils gawk findutils grep jq lz4 gnu-sed watchman
# For Intel Macs
brew update && brew install binutils coreutils gawk findutils grep jq lz4 gnu-sed watchman
Install Vega SDK:
- Close VS Code before installing
- Download and run installer
- Add export commands to shell config
- Verify:
kepler --version
Create Vega Project:
cd apps
kepler project generate \
--template hello-world \
--name vega \
--packageId com.yourcompany.yourapp.vega \
--outputDir vega
6. Configure Metro Bundler
metro.config.js (in each app):
const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');
const config = getDefaultConfig(__dirname);
// Add monorepo support
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '../..');
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(workspaceRoot, 'node_modules'),
];
module.exports = config;
Vega to Multi-TV Migration Guide
Prerequisites
- Node.js 18+
- Yarn 4.x (Berry)
- Java 17 (for Android builds)
- Android SDK with TV emulator
- Xcode (for Apple TV builds)
Phase 1: Monorepo Setup
1.1 Initialize Yarn Workspaces
Create root package.json:
{
"name": "@mycompany/multi-tv",
"version": "1.0.0",
"private": true,
"packageManager": "yarn@4.5.0",
"workspaces": [
"apps/*",
"packages/*"
],
"resolutions": {
"metro-source-map": "0.80.12"
}
}
CRITICAL: The metro-source-map resolution is required to fix a babel plugin error. Without it, you'll get:
.plugins[0] must be a string, object, function
1.2 Create Root Babel Config
CRITICAL: Create babel.config.js at monorepo root. Metro looks for babel config starting from the project root.
const path = require('path');
module.exports = function (api) {
api.cache(true);
// Try to resolve reanimated plugin from the universal TV app
let reanimatedPlugin;
try {
reanimatedPlugin = require.resolve('react-native-reanimated/plugin', {
paths: [path.join(__dirname, 'apps/universal-tv/node_modules')]
});
} catch (e) {
try {
reanimatedPlugin = require.resolve('react-native-reanimated/plugin');
} catch (e2) {
reanimatedPlugin = null;
}
}
const plugins = reanimatedPlugin ? [reanimatedPlugin] : [];
return {
presets: ['babel-preset-expo'],
plugins,
};
};
Phase 2: Shared UI Package Setup
Platform-Specific File Resolution
| Extension | Platform | Description |
|---|---|---|
.ts / .tsx |
All | Base/shared implementation |
.native.ts |
React Native | Android TV, Apple TV, Fire TV FOS |
.android.ts |
Android | Android TV, Fire TV FOS |
.ios.ts |
iOS/tvOS | Apple TV |
.kepler.ts |
Vega | Fire TV Vega OS specific |
.web.ts |
Web | Web TV specific |
CRITICAL - Avoiding Circular Dependencies:
When you have a base class that platform-specific files extend, Metro's platform resolution can create a circular dependency.
# BAD - Creates circular dependency
RemoteControlManager.ts # Base class
RemoteControlManager.android.ts # Imports from RemoteControlManager.ts
FIX: Rename base files to use .base.ts extension:
# GOOD - No circular dependency
RemoteControlManager.base.ts # Base class
RemoteControlManager.android.ts # Imports from RemoteControlManager.base.ts
RemoteControlManager.ios.ts # Imports from RemoteControlManager.base.ts
Phase 3: Universal TV App Setup (Expo)
Fix HMRClient Error (CRITICAL)
CRITICAL: The react-native-tvos fork has an HMR (Hot Module Replacement) incompatibility.
Fix: Add HMRClient polyfill to apps/universal-tv/index.js:
import { registerRootComponent } from 'expo';
import { AppRegistry } from 'react-native';
import App from './App';
// Polyfill for HMRClient to prevent "Module has not been registered as callable" error
if (!AppRegistry.getRunnable('HMRClient')) {
AppRegistry.registerCallableModule('HMRClient', {
setup: () => {},
enable: () => {},
disable: () => {},
});
}
registerRootComponent(App);
Configure app.json
{
"expo": {
"name": "MyTVApp",
"slug": "my-tv-app",
"version": "1.0.0",
"orientation": "landscape",
"userInterfaceStyle": "dark",
"plugins": [
"@react-native-tvos/config-tv"
]
}
}
Phase 4: Platform Abstraction Layer
Remote Control Manager Pattern
Create base class in packages/shared-ui/src/app/remote-control/RemoteControlManager.base.ts:
export type RemoteKey =
| 'up' | 'down' | 'left' | 'right'
| 'select' | 'back'
| 'play' | 'pause' | 'playPause'
| 'fastForward' | 'rewind'
| 'menu';
export interface RemoteKeyEvent {
key: RemoteKey;
action: 'keyDown' | 'keyUp';
timestamp: number;
}
export type RemoteKeyHandler = (event: RemoteKeyEvent) => void;
export interface RemoteControlManagerInterface {
initialize(): void;
cleanup(): void;
addKeyListener(handler: RemoteKeyHandler): () => void;
removeKeyListener(handler: RemoteKeyHandler): void;
}
export class BaseRemoteControlManager implements RemoteControlManagerInterface {
protected listeners: Set<RemoteKeyHandler> = new Set();
initialize(): void {
// Override in platform-specific implementation
}
cleanup(): void {
this.listeners.clear();
}
addKeyListener(handler: RemoteKeyHandler): () => void {
this.listeners.add(handler);
return () => this.removeKeyListener(handler);
}
removeKeyListener(handler: RemoteKeyHandler): void {
this.listeners.delete(handler);
}
protected notifyListeners(event: RemoteKeyEvent): void {
this.listeners.forEach((handler) => handler(event));
}
}
Video Player Service Pattern
Create interface in packages/shared-ui/src/services/videoPlayer/VideoPlayerService.ts:
export interface VideoSource {
uri: string;
type?: 'hls' | 'dash' | 'mp4';
headers?: Record<string, string>;
drm?: DRMConfig;
}
export interface DRMConfig {
type: 'widevine' | 'fairplay' | 'playready';
licenseServer: string;
headers?: Record<string, string>;
}
export interface PlaybackState {
isPlaying: boolean;
isPaused: boolean;
isBuffering: boolean;
currentTime: number;
duration: number;
}
export interface VideoPlayerService {
initialize(): Promise<void>;
load(source: VideoSource): Promise<void>;
play(): Promise<void>;
pause(): Promise<void>;
seek(timeInSeconds: number): Promise<void>;
destroy(): Promise<void>;
getPlaybackState(): PlaybackState;
}
export const DEFAULT_SEEK_SECONDS = 10;
Create native implementation in VideoPlayerService.native.ts (uses react-native-video).
Create Vega implementation in VideoPlayerService.kepler.ts (uses Shaka/W3C Media).
Migration Troubleshooting
Error: .plugins[0] must be a string, object, function
Cause: metro-source-map version mismatch in monorepo
Fix: Add resolution in root package.json:
"resolutions": {
"metro-source-map": "0.80.12"
}
Error: Super expression must either be null or a function
Cause: Circular dependency in class inheritance due to Metro's platform resolution
Fix: Rename base class files from ClassName.ts to ClassName.base.ts
Error: HMRClient.setup() Module has not been registered
Cause: react-native-tvos HMR incompatibility with Expo
Fix: Add HMRClient polyfill in index.js
Error: Cannot find module '@mycompany/shared-ui'
Cause: Metro not configured for monorepo workspace resolution Fix:
- Add
watchFoldersin metro.config.js - Add
extraNodeModulesmapping - Run
yarn installfrom root
Warning: Require cycle: X.ts -> X.ts
Cause: Platform-specific file importing itself due to Metro resolution
Fix: Rename base file to use .base.ts extension
Version Compatibility Matrix
| Package | Version | Notes |
|---|---|---|
| expo | ~51.0.0 | Required for TV support |
| react | 18.2.0 | Must match expo requirement |
| react-native-tvos | ~0.74.3-0 | Use instead of react-native |
| metro-source-map | 0.80.12 | Force via resolution |
| react-native-reanimated | ~3.10.1 | For animations |
| @react-native-tvos/config-tv | ^0.0.12 | Expo plugin for TV |
| react-tv-space-navigation | ^3.6.1 | Spatial navigation |
| Java | 17 | Required for Android Gradle |
| Node.js | 18+ | Required for Expo |
| Yarn | 4.x | Berry with workspaces |
Best Practices
TV-Specific Design
- Always test on actual TV hardware, not just emulators
- Maintain 10-foot UI standards (large text, high contrast)
- Position critical UI within safe zones (48px horizontal, 27px vertical)
- Provide clear focus indicators (borders, scale, highlights)
- Design for remote control first, touch second
Focus Management
- Set default focus on screen mount
- Restore focus when returning from navigation
- Prevent focus traps (ensure all UI is reachable)
- Test navigation flow with D-pad only
- Handle edge cases (first/last item in grid)
Performance
- Optimize image sizes for TV resolution (1920x1080)
- Use FlatList for long scrolling lists
- Implement image caching strategy
- Lazy load video content
- Monitor memory usage on low-end devices
Platform-Specific Code
- Use file extensions for platform variants
- Keep shared logic in base files
- Document platform differences
- Test each platform independently
- Abstract platform APIs behind common interfaces
Video Player
- Always show buffering state
- Handle network failures gracefully
- Implement playback controls with remote support
- Test with various video formats and codecs
- Support multiple video qualities/bitrates
Content Loading
- Implement retry logic for failed requests
- Show loading states during fetch
- Cache catalog data locally
- Handle empty states gracefully
- Validate API response structure
Testing
Unit Tests
- Test business logic in shared-ui package
- Mock platform-specific modules
- Test remote control event handlers
- Validate navigation flows
- Test data transformations
Integration Tests
- Test cross-platform file resolution
- Validate navigation between screens
- Test video player controls
- Verify focus management
- Test catalog API integration
Platform Testing
- Test on Android TV emulator and device
- Test on Apple TV simulator and device
- Test on Fire TV device
- Test on web browser with keyboard navigation
- Verify remote control functionality per platform
Troubleshooting
Metro Bundler Issues
- Clear cache:
yarn start --reset-cache - Check for port conflicts (default 8081)
- Verify workspace resolution in metro.config.js
Platform-Specific Build Failures
- iOS/tvOS: Check Node binary path in
.xcode.env.local - Android TV: Verify Android SDK and emulator setup
- Vega: See Vega-specific troubleshooting below
Vega SDK Issues
Installation Problems
'Electron quit unexpectedly' during installation
- This is expected if VS Code is running during installation
- Restart VS Code after installation completes
Vega SDK not found / kepler command not available
- Verify:
kepler --version - Check environment variables:
echo $KEPLER_SDK_PATH echo $PATH | grep kepler - Add exports to shell config if missing
Development utilities missing (macOS)
brew update && brew install binutils coreutils gawk findutils grep jq lz4 gnu-sed watchman
Build Failures
npm run build:app fails
- Ensure dependencies installed:
npm install - Check Node.js version:
node --version(requires 16.x or later) - Verify package ID format (reverse DNS, no
com.amazon) - Check Metro not already running on port 8081
VPKG file not generated
- Check build output:
build/<architecture>-<buildType>/ - Try clean build:
rm -rf build && npm run build:app
Runtime Issues
App crashes on Vega device
- Check device logs for errors
- Verify VPKG architecture matches device
- Test with debug build first
- Ensure native dependencies compatible with Vega SDK
Metro bundler connection issues
- Verify Metro is running:
yarn start - Check firewall settings
- Ensure device and dev machine on same network
Navigation Issues
- Verify React Navigation installation
- Check focus restoration logic
- Test with D-pad/arrow keys only
- Enable spatial navigation debug mode
Video Playback Issues
- Verify video URL accessibility
- Check video format compatibility (MP4 recommended)
- Test with different video codecs
- Monitor network bandwidth
Additional Resources
Sample Repositories
React Native Multi-TV App Sample
- Repository: https://github.com/AmazonAppDev/react-native-multi-tv-app-sample
- Complete monorepo setup
- Working implementations for all platforms
- Reference for best practices
Official Documentation
Expo TV Documentation
- System requirements and prerequisites
- Quick start with TV templates
- EAS Build profiles for TV
- Platform-specific build instructions
Vega SDK Documentation
- Installation guide
- Vega CLI guide
- Device setup
- Deployment to Amazon Appstore
External Resources
- React Native TV (react-native-tvos)
- React TV Space Navigation
- Expo Examples - TV
- Expo Examples - TV Router
- React Navigation
- React Native Directory
Adding New Features
New Shared Component
- Create in
packages/shared-ui/src/components/ComponentName.tsx - Export from
packages/shared-ui/src/index.ts - Import in apps:
import { ComponentName } from '@project/shared-ui'
Platform-Specific Implementation
- Create base file:
Feature.base.ts - Create platform files:
Feature.android.ts,Feature.ios.ts,Feature.kepler.ts - Metro automatically resolves correct file at build time
New Screen
- Create in
packages/shared-ui/src/screens/ScreenName.tsx - Add to navigation in
navigation/AppNavigator.tsx - Export from
packages/shared-ui/src/index.ts - Use in app's navigation configuration
Next Steps
Recommended Path (Quick Start)
- Clone the repository -
git clone https://github.com/AmazonAppDev/react-native-multi-tv-app-sample.git - Install dependencies -
yarn install && yarn bootstrap - Review the codebase
- Review documentation
- Review steering files
- Configure for Expo TV
- Install Vega SDK (optional)
- Test on platforms
- Customize for your project
- Deploy
Alternative Path (Build from Scratch)
Follow the step-by-step "Build from Scratch" guide above to understand:
- Monorepo setup with Yarn workspaces
- Expo TV configuration
- Vega SDK integration
- Shared component library architecture
- Platform-specific file resolution
- Theme system implementation
- Spatial navigation setup
- Remote control integration
For detailed implementation guides, see:
multi-tv-builder/POWER.md- Complete implementation guidemulti-tv-builder/steering/product.md- Product overviewmulti-tv-builder/steering/structure.md- Project structure detailsmulti-tv-builder/steering/tech.md- Technology stack referencemulti-tv-builder/steering/VEGA_TO_MONOREPO_MIGRATION_GUIDE.md- Migration guide