Skip to main content

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 CPListTemplate at maximumItemCount (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 onBrowseRequest drilldown returns over the cap, the library emits a once-per-node console.warn prefixed [react-native-queue-player] — so you see it in Metro without reaching for native log tooling.
  • Native logs. CarPlay logs at error level in Console; Android Auto logs a warning under the QueuePlayer.Auto tag (this also covers a static setBrowseSnapshot section 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 (AZ, #), 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 CPListTemplate is 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.