Android Auto & CarPlay
RNTP V5 includes full Android Auto and CarPlay support. Users can browse and play audio from their car dashboard without touching their phone.
Setup
1. Declare the media browser service
In your AndroidManifest.xml, add the androidx.media3.session.MediaSessionService declaration:
<service
android:name="com.doublesymmetry.trackplayer.service.HeadlessJsMediaService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service> 2. Add the automotive app descriptor
Create res/xml/automotive_app_desc.xml:
<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="media" />
</automotiveApp> Reference it in AndroidManifest.xml:
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" /> Setting the browse tree
Android Auto displays a browse tree when the user opens your app on the dashboard. Configure it with setBrowseTree:
import TrackPlayer, { BrowseCategory } from '@rntp/player';
await TrackPlayer.setBrowseTree([
{
mediaId: 'recent',
title: 'Recently Played',
items: [
{
mediaId: 'track-1',
url: 'https://example.com/track1.mp3',
title: 'Episode 42',
artist: 'My Podcast',
artworkUrl: 'https://example.com/artwork.jpg',
},
],
},
{
mediaId: 'favorites',
title: 'Favorites',
items: [...],
},
]); BrowseCategory
| Field | Type | Description |
|---|---|---|
mediaId | string | Unique category identifier |
title | string | Category display name |
items | BrowseItem[] | Items in this category — tracks or browsable containers |
BrowseItem
Each item is either playable (has url) or browsable (has children):
| Field | Type | Description |
|---|---|---|
mediaId | string | Unique identifier |
title | string | Display title |
artist | string? | Artist or subtitle |
artworkUrl | string? | Artwork image URL |
url | string? | Audio URL — makes this item playable |
duration | number? | Duration hint in seconds |
isLive | boolean? | Whether this is a live stream |
children | BrowseItem[]? | Child items — makes this item browsable |
Nested browsing
Items can contain children to create drill-down navigation (e.g. albums containing tracks):
TrackPlayer.setBrowseTree([
{
mediaId: 'music',
title: 'Music',
items: [
{
mediaId: 'album-1',
title: 'Chill Vibes',
artist: 'Various Artists',
artworkUrl: 'https://example.com/album-art.jpg',
children: [
{
mediaId: 'track-1',
url: 'https://example.com/track1.mp3',
title: 'Sunset',
artist: 'Artist A',
},
{
mediaId: 'track-2',
url: 'https://example.com/track2.mp3',
title: 'Moonrise',
artist: 'Artist B',
},
],
},
],
},
]); When a user selects a playable item, its siblings become the queue. Maximum nesting depth is 4 levels.
iOS — CarPlay
CarPlay lets users browse and play audio from your app on the car’s built-in display. CarPlay requires adopting the iOS scene lifecycle — your app needs a PhoneSceneDelegate for the phone UI and RNTP provides RNTPCarPlaySceneDelegate for the car display.
1. Add the CarPlay entitlement
In Xcode, open your target’s Signing & Capabilities tab and add the CarPlay Audio entitlement. This inserts the following into your .entitlements file:
<key>com.apple.developer.carplay-audio</key>
<true/> 2. Adopt the scene lifecycle
CarPlay uses iOS scenes. This means your app’s window creation must move from AppDelegate to a PhoneSceneDelegate. This is a one-time migration.
Update AppDelegate.swift — remove the window creation, keep only the factory setup:
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var reactNativeDelegate: ReactNativeDelegate?
var reactNativeFactory: RCTReactNativeFactory?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory
return true
}
} Create PhoneSceneDelegate.swift in your app target — this takes over window creation:
import UIKit
class PhoneSceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let factory = appDelegate.reactNativeFactory else { return }
let window = UIWindow(windowScene: windowScene)
factory.startReactNative(
withModuleName: "YourAppName",
in: window,
launchOptions: nil
)
self.window = window
window.makeKeyAndVisible()
}
} 3. Add the scene manifest to Info.plist
Register both the phone and CarPlay scenes:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Phone</string>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).PhoneSceneDelegate</string>
</dict>
</array>
<key>CPTemplateApplicationSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>CarPlay</string>
<key>UISceneClassName</key>
<string>CPTemplateApplicationScene</string>
<key>UISceneDelegateClassName</key>
<string>RNTPCarPlaySceneDelegate</string>
</dict>
</array>
</dict>
</dict> 4. Set the browse tree
CarPlay uses the same setBrowseTree() call as Android Auto — no extra code required:
TrackPlayer.setBrowseTree([
{
mediaId: 'recent',
title: 'Recently Played',
items: [...],
},
{
mediaId: 'favorites',
title: 'Favorites',
items: [...],
},
]); CarPlay behavior
- Tabs — Each top-level category appears as a tab. CarPlay supports a maximum of 4 tabs; if you provide more, a “More” tab is automatically created for the overflow.
- Playback — Selecting an item starts playback natively without any extra handling in your app.
- Now Playing — The Now Playing screen is provided automatically by CarPlay using the metadata you set via
MPNowPlayingInfoCenter(RNTP keeps this up to date). - Item limits — The number of items displayed per list varies by car manufacturer and CarPlay version. Keep lists concise where possible.