Browsing large catalogs on the car screen
CarPlay and Android Auto are built for short, glanceable lists — not a flat list of thousands of albums. This guide covers the per-node item cap the library enforces and the hierarchical-browse pattern you use to expose a large catalog cleanly on both platforms.
The per-node item cap (500)
Each browsable node returns at most 500 items on both platforms.
- CarPlay hard-caps a
CPListTemplateatmaximumItemCount(500 on current iOS). Large-list handling beyond that is an Apple-private capability not available to third-party apps. - Android Auto requests whole nodes (it asks for the full children list, not a page), so without a cap a 3,000-item node would push all 3,000 rows to the head unit.
The library applies the same 500 ceiling on both. If a node you return exceeds
it, the extra items are dropped and a loud developer log names the offending
parentId and the count. You get it in two places:
- JS console (Metro). When an
onBrowseRequestdrilldown returns over the cap, the library emits a once-per-nodeconsole.warnprefixed[react-native-queue-player]— so you see it in Metro without reaching for native log tooling. - Native logs. CarPlay logs at
errorlevel in Console; Android Auto logs a warning under theQueuePlayer.Autotag (this also covers a staticsetBrowseSnapshotsection over the cap, which the JS warning does not).
Treat either log as a signal to switch the node to a hierarchy.
The hierarchical-browse pattern
Instead of one flat mega-list, expose a catalog as a tree the user drills into.
The library already supports this with no extra API: ship a top-level
BrowseSnapshot via setBrowseSnapshot, and resolve each drilldown lazily in
onBrowseRequest(parentId).
Common groupings:
- A–Z — letter rows (
A…Z,#), then the items starting with that letter. - By artist — artist rows, then that artist's albums, then tracks.
- By genre / by decade — a category row, then its items.
A–Z "jump to letter"
A–Z is the cross-platform equivalent of a fast-scroll index. Neither platform exposes a third-party alphabet scrubber, so a hierarchy of letter nodes is how you get "skip to T" — one extra tap instead of a long drag.
Albums
└─ All Albums (A–Z) ← onBrowseRequest returns A…Z (+ "#") rows
├─ A ← onBrowseRequest("albums-az:A") → A-albums
├─ …
└─ S ← onBrowseRequest("albums-az:S") → S-albums
Build the letter rows in your top-level snapshot (or in onBrowseRequest for
the parent node), then resolve each letter's items lazily when the user opens it.
Sub-bucketing a fat letter
When a single letter has more than 500 items, split it again so each child node stays under the cap — typically by the second character:
S
├─ Sa–Sx ← onBrowseRequest("albums-az:S:a-x")
└─ Sy–Sz ← onBrowseRequest("albums-az:S:y-z")
Pack consecutive second-character groups into non-overlapping ranges, each at or below the cap, so no item is lost or duplicated.
What is not possible for third-party apps
These are platform limits, not library gaps — design around them:
- Alphabet index scrubber — CarPlay's fast-scroll index (
sectionIndexTitles) is Apple-exclusive; Android Auto has no media fast-scroll index. Use an A–Z hierarchy instead. - CarPlay pagination — a
CPListTemplateis a single fixed page; there is no "load more". Stay under the cap with hierarchy. - Android Auto scroll-pagination — the host requests the whole node up front; it does not page as the user scrolls.
The 4-tab root limit
The browse root renders as a tab strip capped at 4 tabs. Ship at most four
top-level BrowseSections; fold anything beyond that into in-tab drilldown. Over
the cap, the library truncates to the first four and logs a warning.