Leanback to Compose for TV Migration
Migrate an Android TV app from the legacy Leanback UI Toolkit to Jetpack Compose for TV (androidx.tv) — the 10-foot UI design constraints, D-pad focus handling (initial focus, bidirectional routing, focusRestorer, IME focus chaining), Media3 playback with PlayerSurface, a five-phase migration order, and a direct Leanback-class-to-Compose-screen mapping table.
Porting a phone Compose screen straight to Android TV breaks on day one — nothing receives initial D-pad focus, carousels forget which card you were on when you navigate back to them, and a text field silently refuses to open the keyboard, because TV Compose needs explicit focus management that mobile Compose never required.
Who it's for: Android engineers migrating a Leanback-based TV app to Jetpack Compose for TV, teams retiring BrowseSupportFragment/ArrayObjectAdapter/CardPresenter in favor of androidx.tv.material3, developers debugging D-pad focus traps or IllegalStateException from an off-screen FocusRequester, anyone building Media3-based video playback for Android TV with Compose
Example
"Migrate our BrowseSupportFragment screen to Compose for TV" → A BrowseScreen built from LazyColumn-of-LazyRows with androidx.tv.material3 components, Modifier.focusRestorer() on each row so vertical navigation between carousels doesn't lose the last-focused card, initial focus wired via FocusRequester in a LaunchedEffect, and the five-phase migration order that replaces legacy Fragments without breaking compilation mid-way
CLAUDE.md Template
New here? 3-minute setup guide → | Already set up? Copy the template below.
npx ccplaybooks add leanback-to-compose-tv-migration# Leanback to Compose for TV Migration
Migrate an Android TV application from the legacy Leanback UI Toolkit, Android Views, or Support Fragments to Jetpack Compose for TV (`androidx.tv`) — covering browse screens, settings screens, authentication screens, and video playback screens, and replacing `BrowseSupportFragment`, `LeanbackSettingsFragment`, `PreferenceFragment`, `VideoSupportFragment`, `GuidedStepSupportFragment`, `SearchSupportFragment`, `VerticalGridSupportFragment`, `Presenter`, `ArrayObjectAdapter`, and `CursorMapper` with modern Compose equivalents.
## The 10-Foot UI
A "10-foot UI" is a design paradigm for televisions, tailored for viewing from roughly 3 meters away:
- **Viewing distance** — text and UI elements must be comfortably readable from a distance; layouts stay uncluttered, without dense blocks of text.
- **Color contrast** — high-contrast palettes and distinct visual indicators keep focused states visible across different TV panels, which often have lower display contrast.
- **D-pad navigation** — interaction relies on a directional remote with limited 4-way navigation (up/down/left/right); components are organized into clear spatial grids and carousels without focus traps.
Get new playbooks like this one
One email a week with new Claude Code workflows. Free, like everything here.
No spam. Unsubscribe anytime.
What This Does
A migration playbook, from Google's own android/skills repository, for moving an Android TV application off the legacy Leanback UI Toolkit and onto Jetpack Compose for TV (androidx.tv). It starts from the "10-foot UI" design paradigm that TV interfaces have to satisfy — comfortable readability from across a room, high-contrast focus states, and D-pad-only 4-way navigation with no focus traps — and works through the specific engineering rules that make Compose actually work for that paradigm: androidx.tv.material3 over mobile Material3 for built-in D-pad focus handling, explicit initial-focus assignment via FocusRequester (TV Compose never focuses anything automatically), and Modifier.focusRestorer() on every horizontal carousel so vertical navigation between rows doesn't lose the user's place — the exact bug that produces the most confusing TV-Compose focus complaints.
It covers Media3 video playback migration (PlayerSurface plus a mandatory transport-controls overlay, D-pad-driven seeking via onPreviewKeyEvent, never a legacy AndroidView-wrapped PlayerView), text input and IME focus chaining for search bars and login forms (including why intercepting the Back key during text editing is mandatory, not optional), a five-phase migration order designed to avoid breaking compilation mid-migration (foundation → leaf screens → browsing screens → details/playback → final cleanup), and a direct mapping table from every major Leanback class (BrowseSupportFragment, ArrayObjectAdapter, CardPresenter, GuidedStepSupportFragment, and more) to its modern Compose equivalent.
Quick Start
Step 1: Create a Project Folder
mkdir tv-migration && cd tv-migration
Step 2: Download the Template
Click Download above, then:
mv ~/Downloads/CLAUDE.md ./
Step 3: Migrate a Screen
claude
Point Claude at a Leanback-based screen (a BrowseSupportFragment, a settings screen, a video-playback screen) and ask it to migrate that screen to Compose for TV. It will apply the correct focus-handling patterns, the right androidx.tv.material3 components, and place the work at the correct phase of the overall migration order.
Tips & Best Practices
- Attach
Modifier.focusRestorer()to everyLazyRowcarousel from the start — retrofitting it after users report "focus jumps around" is far more work than including it in the first pass at each screen. - Assign initial focus with a
LaunchedEffect(Unit)(or keyed to relevant state for pagers) on literally every screen — a screen with no initial focus assignment leaves the D-pad with nothing to move from, which reads as a completely broken screen to a TV user. - Follow the five-phase order even under deadline pressure: migrating browsing/discovery screens (Phase 3) before the foundational design system and leaf screens (Phases 1–2) tends to produce rework once the shared
TvThemeand atomic components stabilize.
Limitations
- Targets Android TV specifically via
androidx.tv.*— the focus-handling patterns don't transfer to mobile Compose, where initial and directional focus work differently. - Assumes the app can adopt Media3 for playback; apps on older playback stacks need that migration handled as a prerequisite, not covered here.
- The component mapping table covers the common Leanback surface (
BrowseSupportFragment,DetailsSupportFragment,GuidedStepSupportFragment, and similar) — a heavily customizedPresentersubclass hierarchy may need additional analysis beyond the direct 1:1 mappings shown.