When building TV applications, youβve likely faced the challenge of maintaining separate codebases for Fire TV (both Fire OS and Vega OS), Android TV, Apple TV, and others. Each Operating System (OS) has specific native implementations, but much of your application code (i.e. navigation, state management, business logic, UI components, etc.) may be fundamentally the same across all of them and could be shared.
For those who onboarded to React Native through Vega, we want to provide a path to reuse that investment across other targets (beyond FOS and Vega) and help you lower your total cost per OS.
This is made possible by refactoring a Vega project to a βmonorepoβ-style project architecture, which breaks out shared components/logic and Vega-specific components/logic, along with other OS targets (like Android TV); with the goal of sharing 70+% of existing code. Recently we have converted sample Vega projects to support Android TV and Apple TV (through ExpoTV), ran hands-on workshops with developers, refined our approach, and weβre ready to share what we have learned.
In this post we will cover:
- The shared workspace architecture β how to structure your codebase so shared code lives in one place while OS-specific implementations (video, Amazon features, native integrations) remain separate
- One hands-on example β a new project to experience the concept in minutes
- An Agent Skill - You can use today with Kiro, Claude, or other AI coding assistants to migrate your own app
The skill will guide you through: (1) analyzing your codebase to estimate code reuse and provide refactoring suggestions before beginning, (2) migrating your codebase to the shared workspace structure, and (3) adding Fire OS/Android TV and Apple TV builds (via Expo TV). Partners in our workshops converted production apps in less than a day using this process; work that would normally take weeks. To set expectations: the prompts create a solid scaffold that gets you running on multiple OSβs quickly, but you will still need testing, refinement, and production hardening. Weβll cover what to expect in detail below.
The shared workspace approach
To enable code reuse across OSβs, we start by refactoring the project structure into three separate workspaces: one for Vega-specific code (and components), one for shared OS-agnostic code, and one for Expo TV (supporting Android TV and Apple TV). Each workspace is its own independent project with its own package.json, containing its specific dependencies and build configuration.
A parent project sits at the root level, coordinating and building across these sub-projects on demand. We are using Yarn v4 workspaces to manage this architecture because it provides efficient dependency management, enables cross-workspace imports, and allows each OS to maintain independent versioning while sharing common code. This creates a clean separation between shared code and OS-specific implementations, which also allows the ability to add new OSs in the future. See below for a sample project structure:
my-app/
βββ package.json # Root workspace config (Yarn 4.x)
βββ packages/
β βββ shared/ # @myapp/shared β OS-agnostic code
β β βββ package.json # peerDependencies only
β β βββ index.ts # Barrel exports
β β βββ src/
β β βββ components/ # Shared UI (Banner.tsx, Banner.kepler.tsx, Banner.android.tsx)
β β βββ hooks/ # Custom React hooks
β β βββ services/ # Business logic
β β βββ utils/ # Helpers (scaling, formatting)
β βββ vega/ # @myapp/vega β Fire TV
β β βββ src/App.tsx
β β βββ manifest.toml
β β βββ metro.config.js
β βββ expotv/ # @myapp/expotv β Android TV / Apple TV / Web
β βββ src/App.tsx
β βββ app.json
β βββ metro.config.js
The shared package contains all the code that can be shared across multiple targets. The vega and expotv packages are thin wrappers that handle navigation setup, authentication, data fetching, and OS-specific native integrations. Within expotv you will find the OS specific features for both Fire OS and AndroidTV within it. OS packages import from shared, but shared never imports from OS packages. With this pattern you can expand your RN app to other OSs if desired, i.e. tvOS, Tizen, etc.
Summary: benefits of the shared workspace approach
| Benefit | What It Means |
|---|---|
| Cleaner separation | Shared vs OS-specific code is explicit |
| Simpler debugging | OS issues are contained to their folder |
| Code reuse | 70-85% of code shared across all OSβs |
| Fast Refresh | Save once, see updates on every running OS simultaneously |
| Independent versioning | Each OS can use different React Native versions |
| Isolated builds | No cross-OS build conflicts |
| Easy scaling | Adding Apple TV, Web, or Tizen is adding another package |
Why shared workspace instead of adding Android as a build target? You could add Android support directly to your existing Vega project, but the shared workspace approach provides cleaner separation, simpler debugging (OS issues stay contained to their folder), and makes it straightforward to add targets beyond Android. Additionally, this scales beyond Vega and Android TV/Fire OS, and allows you to share code across other OSβs.
What can be shared, What cannot (Yet)
Our strategy involves using React Native as our software framework of choice for all development and deployment workflows to build app experiences that can be natively compiled for and deployed to different device targets (profile + OS). This means that we have to deal with code that can be shared, and OS specific components (think of Alexa for Fire TV or Siri for tvOS). Although we have a good amount of code that can be shared, we recommend keeping separate video playback and Amazon specific features that have OS specific implementations (Content Launcher, etc.).
OS-Agnostic (Shared) Code (70-85% of typical apps)
| Category | Examples |
|---|---|
| Navigation patterns | Screen flows, routing logic |
| Data fetching and state management | API calls, caching, Redux/Zustand stores |
| UI components | Buttons, cards, lists, layouts, modals |
| Business logic and utilities | Validation, formatting, calculations |
| Styling and theming | With scaling utilities for different TV displays |
OS-Specific Code (fork per OS for now)
Video playback: production streaming apps tend to use OS-specific media players for optimal performance. You can either follow that path, using native implementations per OS, or use an abstraction to work across like react-native-video (which now has Vega support). Unless you use these abstractions, your media implementations should live in your OS-specific packages (vega/, expotv/), not in shared/.
Amazon features: Amazon specific features such as Content Launcher, In-App Purchase, Amazon Device Messaging, etc. currently require separate implementations for Vega and Fire OS because their underlying APIs are OS-specific. Weβre working to harmonize Amazon features across Fire OS and Vega, exposing a single RN library for each feature that youβll be able to integrate in the shared folder. Until then, fork these implementations and structure them for easy consolidation later.
UI components: Use standard React Native primitives for shared UI. Platform-specific styling can be handled via platform-specific file extensions (.android.tsx, .kepler.tsx) or Platform.select(). If youβre using Kepler UI Components (KUIC): simple components like Button and Text migrate easily, while Carousel and SeekBar require additional work. Identify these dependencies early in your analysis phase. Our first prompt (migration analysis) helps with this and gives you a complete list of what can be migrated and how.
Interested in Trying it out? Let us know
Weβre ready to share what weβve built, and we want your feedback: what works for your apps, what doesnβt, and what youβd like to see next. Our next section will cover how you can get started with your own app. Weβll also be publishing these tools as agent-skills on GitHub, so youβll be able to contribute directly, i.e. whether thatβs adding support for new OSβs, improving the migration prompts, or something we havenβt thought of yet.
Getting started with shared workspaces
What youβll need
Before getting started, make sure you have:
- Node.js 20+
- Yarn 4.x (Berry) β installation guide
- For Vega: Vega CLI and Vega development environment
- For Android TV: Android Studio with an Android TV system image (API Level 34+)
- For Apple TV (Mac only): Xcode with tvOS 26+ support
Check your environment first
Common issues when building and running each target for the first time include a misconfigured ANDROID_HOME path, outdated Java versions, and legacy Vega SDK references lingering in your PATH. Before running the sample apps or migration prompts, run our environment checker script to catch these early:
- Expand the environment checker script below and copy its contents
- Create a file named
check-dev-environment.shand paste the contents - Run the following commands:
chmod +x check-dev-environment.sh
./check-dev-environment.sh
Environment checker script: copy and paste into an .sh file
#######################################
# Development Environment Check Script - Amazon Devices 02/2026
# Checks Java version, Kepler/Vega SDK, and PATH configuration
#######################################
# Configuration Variables - Modify these as needed
MIN_JAVA_VERSION=17
MIN_KEPLER_VERSION="0.21"
WARN_ON_MULTIPLE_JAVA=true
CHECK_KNTOOLS=true
CHECK_KEPLER_PATH=true
# Color codes for output
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Disable colors if not in a terminal
if [ ! -t 1 ]; then
RED=''
YELLOW=''
GREEN=''
BLUE=''
NC=''
fi
#######################################
# Functions
#######################################
print_header() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
}
print_success() { echo -e "${GREEN}β${NC} $1"; }
print_warning() { echo -e "${YELLOW}β ${NC} $1"; }
print_error() { echo -e "${RED}β${NC} $1"; }
print_info() { echo -e "${BLUE}βΉ${NC} $1"; }
compare_versions() {
local ver1=$1 ver2=$2
ver1=${ver1#v}
ver2=${ver2#v}
if [ "$ver1" = "$ver2" ]; then return 0; fi
local IFS=.
local i ver1_arr=($ver1) ver2_arr=($ver2)
for ((i=${#ver1_arr[@]}; i<${#ver2_arr[@]}; i++)); do ver1_arr[i]=0; done
for ((i=0; i<${#ver1_arr[@]}; i++)); do
if [[ -z ${ver2_arr[i]} ]]; then ver2_arr[i]=0; fi
if ((10#${ver1_arr[i]} > 10#${ver2_arr[i]})); then return 0; fi
if ((10#${ver1_arr[i]} < 10#${ver2_arr[i]})); then return 1; fi
done
return 0
}
check_java_version() {
print_header "Checking Java Version"
if ! command -v java &> /dev/null; then
print_error "Java is not installed or not in PATH"
return 1
fi
local java_version_output=$(java -version 2>&1)
local java_version=$(echo "$java_version_output" | grep -i version | head -n 1 | sed -E 's/.*version "?([^"]*)"?.*/\1/')
local major_version=""
if [[ $java_version =~ ^1\.([0-9]+) ]]; then
major_version="${BASH_REMATCH[1]}"
elif [[ $java_version =~ ^([0-9]+) ]]; then
major_version="${BASH_REMATCH[1]}"
fi
print_info "Java version: $java_version (Major: ${major_version:-unknown})"
print_info "Java home: ${JAVA_HOME:-Not set}"
print_info "Java location: $(which java)"
if [ -z "$major_version" ]; then
print_error "Could not parse Java version from: $java_version_output"
return 1
fi
if [ "$major_version" -ge "$MIN_JAVA_VERSION" ] 2>/dev/null; then
print_success "Java version $major_version meets minimum requirement (>= $MIN_JAVA_VERSION)"
else
print_error "Java version $major_version is below minimum requirement (>= $MIN_JAVA_VERSION)"
print_warning "Please upgrade to Java $MIN_JAVA_VERSION or higher"
return 1
fi
if [ "$WARN_ON_MULTIPLE_JAVA" = true ]; then
local java_count=$(which -a java 2>/dev/null | wc -l)
if [ "$java_count" -gt 1 ]; then
print_warning "Multiple Java installations detected in PATH:"
which -a java | while read -r java_path; do echo " - $java_path"; done
fi
fi
echo ""
return 0
}
check_kepler_vega() {
print_header "Checking Kepler/Vega SDK Version"
local found_sdk=false sdk_name="" sdk_version=""
if command -v kepler &> /dev/null; then
found_sdk=true
sdk_name="Kepler"
sdk_version=$(kepler -v 2>&1 | head -n 1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || echo "unknown")
print_info "Kepler location: $(which kepler)"
fi
if command -v vega &> /dev/null; then
found_sdk=true
sdk_name="Vega"
sdk_version=$(vega -v 2>&1 | head -n 1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || echo "unknown")
print_info "Vega location: $(which vega)"
fi
if [ "$found_sdk" = false ]; then
print_warning "Neither Kepler nor Vega SDK found in PATH"
return 1
fi
print_info "$sdk_name version: $sdk_version"
if [ "$sdk_version" = "unknown" ]; then
print_error "Could not determine SDK version"
return 1
elif compare_versions "$sdk_version" "$MIN_KEPLER_VERSION"; then
print_success "$sdk_name version $sdk_version meets minimum requirement (>= $MIN_KEPLER_VERSION)"
else
print_error "$sdk_name version $sdk_version is below minimum requirement (>= $MIN_KEPLER_VERSION)"
return 1
fi
echo ""
return 0
}
check_path_references() {
print_header "Checking PATH for Legacy References"
local found_issues=false kntools_found=false kepler_found=false
if [ "$CHECK_KNTOOLS" = true ]; then
if echo "$PATH" | grep -q "\.kntools"; then
print_warning "Found .kntools reference(s) in PATH:"
echo "$PATH" | tr ':' '\n' | grep "\.kntools" | while read -r path_entry; do echo " - $path_entry"; done
found_issues=true
kntools_found=true
else
print_success "No .kntools references found in PATH"
fi
fi
if [ "$CHECK_KEPLER_PATH" = true ]; then
if echo "$PATH" | grep -q "\.kepler"; then
print_warning "Found .kepler reference(s) in PATH:"
echo "$PATH" | tr ':' '\n' | grep "\.kepler" | while read -r path_entry; do echo " - $path_entry"; done
found_issues=true
kepler_found=true
else
print_success "No .kepler references found in PATH"
fi
fi
if [ "$found_issues" = true ]; then
echo ""
print_error "ACTION REQUIRED: Remove legacy PATH references to avoid conflicts"
print_info "Remove them from ~/.bashrc, ~/.zshrc, or ~/.bash_profile"
print_info "After removing, restart your terminal or run: source ~/.bashrc (or ~/.zshrc)"
fi
echo ""
return 0
}
check_android_environment() {
print_header "Checking Android Environment Variables"
local android_home="${ANDROID_HOME:-$ANDROID_SDK_ROOT}"
if [ -n "$ANDROID_HOME" ]; then
print_success "ANDROID_HOME is set: $ANDROID_HOME"
if [ -d "$ANDROID_HOME" ]; then
print_success "ANDROID_HOME directory exists"
else
print_error "ANDROID_HOME directory does not exist"
return 1
fi
else
print_warning "ANDROID_HOME is not set"
if [ -n "$ANDROID_SDK_ROOT" ]; then
print_info "ANDROID_SDK_ROOT is set: $ANDROID_SDK_ROOT"
fi
fi
if [ -n "$JAVA_HOME" ]; then
print_success "JAVA_HOME is set: $JAVA_HOME"
if [ -d "$JAVA_HOME" ]; then
print_success "JAVA_HOME directory exists"
else
print_error "JAVA_HOME directory does not exist"
fi
else
print_warning "JAVA_HOME is not set (may use system default)"
fi
if [ -n "$android_home" ] && [ -d "$android_home" ]; then
if [ "$(ls -A "$android_home" 2>/dev/null)" ]; then
print_success "Android SDK directory contains files"
else
print_warning "Android SDK directory exists but is empty"
fi
fi
echo ""
return 0
}
#######################################
# Main Execution
#######################################
main() {
echo ""
echo -e "${BLUE}ββββββββββββββββββββββββββββββββββββββββββ${NC}"
echo -e "${BLUE}β Development Environment Check β${NC}"
echo -e "${BLUE}ββββββββββββββββββββββββββββββββββββββββββ${NC}"
local exit_code=0
check_java_version || exit_code=$?
check_kepler_vega || exit_code=$?
check_android_environment
check_path_references
print_header "Summary"
if [ $exit_code -eq 0 ]; then
print_success "All critical checks passed!"
else
print_warning "Some checks failed. Please review the warnings above."
fi
echo ""
echo -e "${BLUE}Configuration used:${NC}"
echo " MIN_JAVA_VERSION: $MIN_JAVA_VERSION"
echo " MIN_KEPLER_VERSION: $MIN_KEPLER_VERSION"
echo " WARN_ON_MULTIPLE_JAVA: $WARN_ON_MULTIPLE_JAVA"
echo " CHECK_KNTOOLS: $CHECK_KNTOOLS"
echo " CHECK_KEPLER_PATH: $CHECK_KEPLER_PATH"
echo ""
return $exit_code
}
main
exit $?
The script checks Java version (17+ required), Kepler/Vega SDK version (0.21+ required), ANDROID_HOME and JAVA_HOME configuration, and flags any legacy .kntools or .kepler PATH references that can cause conflicts. If everything passes, youβre ready to go. If not, the script tells you exactly what to fix before you can proceed.
VegaMultiTVHelloWorld
This is the simplest starting point. Itβs a minimal project with a shared Header component that renders across Vega, Android TV, Apple TV, and Web; all from the same codebase. Running and building this app will help you understand the concept of shared workspaces before you try it out in your own codebase.
HelloSharedWorkspace/
βββ packages/
β βββ shared/ # @hellosharedworkspace/shared
β β βββ components/
β β βββ Header.tsx # Shared component with specific target variants
β βββ vega/ # Vega (RN 0.72)
β βββ expotv/ # Fire OS / Android TV / Apple TV (Expo 52, RN 0.76)
Quick start: follow the README.md from the branch, clone the repo and follow these steps (see README.md for expanded details on how to run this)
# Install all workspace dependencies
yarn
# Build and run Fire TV
yarn vega:build
# Build and run Android TV
yarn expotv:prebuild
yarn expotv:android
# Build and run Apple TV (Mac only)
yarn expotv:ios
The βwow momentβ: open the shared Header.tsx, change the text, hit save, and watch Fast Refresh update the app on every running OS simultaneously. You are now sharing code and seeing how your app looks across several OSβs as you code!
Our new VegaSharedWorkspacePlayground app, utilizing shared workspaces, running simultaneously on Vega, AndroidTV, AppleTV, and Web
Applying this approach to your own app: three-step migration
Weβve developed a three-step, AI-assisted migration process packaged as an agent skill that you can use with Kiro, Claude, or other AI coding assistants. The skill guides the AI through each phase of the migration automatically.
Using the agent skill
The skill is available on GitHub: vega-multi-tv-migration
To use it:
- Clone or download the skill from the devices-agent-skills repository
- Copy the `vega-multi-tv-migration` directory into your AI assistantβs skills folder (e.g. `~/.kiro/skills/` for Kiro)
- Open your Vega project and start a conversation with your AI assistant e.g. βAnalyze my Vega app for multi-platform migrationβ. The skill activates automatically based on your conversation context.
The skill contains three phases, each with detailed reference documents, configuration templates, and validation checklists. You work through them in order, and the AI loads the appropriate guidance for each phase.
REMINDER: we are going to turn your current RN app in Vega, into a shared workspace architecture, so it can target multiple OSβs. For this, you will need to have your latest app ready in local, and available to the agent that you choose to run the prompts on. Although you can experiment with the prompts at the moment, it is ideal to clean your repository in case you have known bugs and issues, as these will be propagated across the refactored architecture (see Best practices section for more information)
Step 1: Analyze your codebase
Goal: assess migration viability and create a detailed plan.
The analysis phase performs static code analysis on your existing Vega project and produces:
- An executive summary with estimated code reuse percentage (services, components, screens, utilities)
- Vega dependency classification:
- Category A: JS-only implementations β goes to
shared - Category B: Stock RN replacements available via Vega Module Resolver Preset (VMRP) β likely
shared - Category C: Requires native implementation β stays in OS package
- Category A: JS-only implementations β goes to
- Screen-by-screen dependency mapping with complexity ratings
- Migration phases and effort estimation
Step 1 Prompt Example β Code Analysis
Analyze my Vega app for multi-platform migration.
Step 2: build the shared workspace structure
Goal: convert your Vega project to the shared workspace architecture.
This prompt creates the monorepo scaffold, moves code into shared and vega packages, configures Metro for monorepo resolution, and sets up VMRP (Vega Module Resolver Preset) so shared code uses standard React Native imports while Vega automatically resolves them to Vega equivalents.
After this step, your Vega app should build and run identically to before; but now with shared code properly separated and ready for other OSβs.
Platform-specific code strategies covered:
- File extensions:
.kepler.tsx,.android.tsx,.ios.tsx,.web.tsx - Inline conditionals:
Platform.OSandPlatform.select - Screen container pattern: OS-specific wrappers importing shared components
- VMRP for transparent library mapping
Step 2 Example Prompt β Build Structure
Convert my Vega project to a yarn workspaces monorepo using the analysis from Phase 1.
Step 3: add OS-specific implementations
Goal: get Fire OS/Android TV and Apple TV builds working using Expo TV.
This prompt sets up the Expo TV package and implements replacements for Vega-specific dependencies:
@amazon-devices/kepler-player-client
react-native-video@amazon-devices/kepler-ui-components
Custom components or community libraries@amazon-devices/kepler-file-system
expo-file-system- And moreβ¦
It also covers focus state management for Android TV, tvOS deployment target configuration for Apple TV, and a verification checklist for both operating systems.
Step 3 Example Prompt β OS Implementation
Add Android TV and Apple TV support using Expo TV. Replace my Vega-specific dependencies with stock React Native equivalents.*What can you expect after running the prompts
The three-step migration produces a working shared workspace architecture with your code properly separated into shared and OS-specific packages. Weβve tested this approach with some of our partners, and they were able to convert and run Vega apps to support Vega, Fire OS, and Android TV in hours; work that typically takes two to three months (or a sprint at minimum for a simple POC of one or two screens).
The prompts handle the heavy lifting (architecture, code separation, and OS wiring) in minutes. You will still need to fix edge cases, adjust some imports, and add tests before shipping to production.
If you have an Amazon POC, we recommend running the skill before a workshop session and using the workshop time to refine and validate the result together.
What you get βout of the boxβ:
- A monorepo scaffold with shared, Vega, and Expo TV packages wired up and building
- Your shared code extracted and using standard React Native imports (VMRP handles Vega resolution)
- OS-specific implementations stubbed out and ready to complete
- Vega app that builds and runs identically to before the migration
What youβll need to finish:
- Some manual fixes for edge cases (import adjustments, OS-specific features)
- Testing, refinement, and production hardening
Best practices
- Clean up before migration: AI tools migrate code as-is, i.e. including unused files, unoptimized code, and existing bugs. Invest in an optimization sprint before proceeding: remove dead code, fix known bugs, standardize coding patterns, and update formatting.
- Manage React Native version deltas: OSβs donβt need identical RN versions, but keep the delta to 4-6 versions maximum to maintain third-party library compatibility. For example, Reanimated 3 supports RN through 0.78, but RN 0.80+ requires Reanimated 4 with API changes. If youβre planning a migration, consider upgrading to RN 0.83 (the upcoming Vega version) as part of the process.
- Test on real devices: simulators are great for development, but validate on real hardware before shipping.
- Prebuild targets separately first: start by prebuilding each target independently. Once all are running, you can prebuild together.
Whatβs Coming Next
- Expanding target support: The shared workspace approach works with any OS that supports React Native. Weβre actively working on providing more examples and guides for tvOS (AppleTV) and Web (both supported via ExpoTV) and Tizen (Samsung TV).
Recommended steps to try this out
- Try ReactNativeMultiTvHelloWorld to experience Fast Refresh across Vega, Fire OS, AndroidTV, tvOS & Web in minutes
- Run the skill on your own app
- Share your feedback β what works, what doesnβt, what youβd like to see next
- We welcome community contributions - The skill contains all target-specific patterns, and weβd love to see contributions to add more targets so we can all reuse our RN codebase across OSβs beyond Vega.
Weβre building this with the community, not just for it. If youβre considering multi-target for a production app, weβd love to discuss your specific architecture and requirements.
Drop a comment below or reach out to your Amazon POC to request a workshop deep dive.
Debugging Tips
- If your Android setup isnβt working, check your
ANDROID_HOMEpath - Make sure you have the latest version of Vega Studio and Vega SDK
- Prebuild each OS separately first, then together once all are running
- Focus on Vega and AndroidTV/Fire OS first, add tvOS build/runs later
Last updated: Mar 11, 2026
