---
topic: frontend-insights playlist page components
last_updated: 2026-03-16
---

# Frontend Playlist Page Components

Repo: `/Users/cbeesley/code/frontend-insights`
Main page entry: `src/pages/playlist/playlistPage.tsx`
Page body: `src/pages/playlist/playlistPageBody.tsx`

## Page Architecture

```
PlaylistPage (playlistPage.tsx)
  ├── PageHeader (playlistPageHeader/)
  ├── PlaylistPageHeaderMetrics (playlistPageHeaderMetrics/)
  │   └── smgDonutMetric/
  │   └── playlistMetricsLegendItem/
  └── PlaylistPageBody (playlistPageBody.tsx)
      ├── PlaylistToolbar (playlistToolbar/)
      ├── PlaylistPerformanceOverTime (playlistPerformanceOverTime/)
      │   └── Header (header/)
      │   └── PlaylistDemographics (playlistDemographics/)
      └── PlaylistTracklist (playlistTracklist/)
          └── tracklistTable/
          └── songInfoModal/
```

## Route Parameters

URL pattern: `/playlist/:id` where `id` is the **bare platform playlist ID** — no store prefix for any store.
- Spotify example: `https://insights.theorchard.com/playlist/4OkGCwPHqBxHjqlmTuFhj6`
- Apple Music example: `https://insights.theorchard.com/playlist/pl.c41905dfd06e430281c5560b704bb049?country=SE`

`parseStoreId(id)` is an internal function that extracts `{ storeId, storePlaylistId }` from the raw ID — it does NOT mean the URL contains a `storeId:` prefix. The store is inferred from the ID format (Apple Music IDs start with `pl.`).

`country` query param sets the storefront for Apple Music.

## Key Component Files

| Component | Path |
|-----------|------|
| PlaylistPage | `src/pages/playlist/playlistPage.tsx` |
| PlaylistPageBody | `src/pages/playlist/playlistPageBody.tsx` |
| PlaylistPerformanceOverTime | `src/pages/playlist/components/playlistPerformanceOverTime/playlistPerformanceOverTime.tsx` |
| PlaylistDemographics | `src/pages/playlist/components/playlistDemographics/playlistDemographics.tsx` |
| PlaylistTracklist | `src/pages/playlist/components/playlistTracklist/playlistTracklist.tsx` |
| PlaylistPageHeaderMetrics | `src/pages/playlist/components/playlistPageHeaderMetrics/playlistPageHeaderMetrics.tsx` |
| PlaylistPageHeader | `src/pages/playlist/components/playlistPageHeader/playlistPageHeader.tsx` |
| PlaylistToolbar | `src/pages/playlist/components/playlistToolbar/playlistToolbar.tsx` |

## PoT (Performance Over Time) Chart

Metrics available:

- `STREAMS` — streams with percentage
- `LISTENERS` — listener count
- `FOLLOWERS` — Spotify followers
- `STREAMS_LISTENER` — streams per listener ratio
- `AVG_STREAMS` — daily average streams

Dimensions:

- `TOTAL` — single timeseries line
- `MARKET` (by country) — bar/line by country
- `DAILY_CHANGE` — only for Followers metric

GraphTypes: `AREA` (default), `LINE`, `COLUMN`

- `FOLLOWERS + DAILY_CHANGE` → `LINE`
- `MARKET + STREAMS/LISTENERS` → `COLUMN`
- All else → `AREA`

### Demographics

When `dimension=TOTAL` and `metric=STREAMS`, a `PlaylistDemographics` panel renders inline in the legend.
Demographics uses `countries` param as stream countries filter (IN-16696).

### Export

Export via download glyph button:

- `TOTAL` dimension → exports total metrics
- Other dimensions → exports PoT series

## Storefront Feature

Added IN-16755 (FE commit `f3c5ab9fb`):

- `storefront` param passed from URL `country` to `usePlaylistMetadataQuery`
- `usePerformanceOverTimeQuery` receives `storefront: countries[0]`
- `INSIGHTS_STORE_FRONT_FIX = 'insights_store_front_fix'` gates the storefront path in some components

## Feature Flags (Split.io)

Defined in `src/constants/featuresFlags.ts`:

| Constant | Split.io Name | Purpose |
|----------|--------------|---------|
| `INSIGHTS_PLAYLIST_PAGE_NAVIGATION` | `insights_playlist_page_navigation` | Playlist page nav |
| `INSIGHTS_PLAYLISTS_STORE_GRAPHS` | `insights_playlists_store_graphs` | Store breakdown graphs |
| `INSIGHTS_STORE_FRONT_FIX` | `insights_store_front_fix` | Storefront parameter handling |
| `INSIGHTS_STORE_FILTER_HIDDEN_APPLY_BUTTON` | `insights_store_filter_hidden_apply_button` | Store filter UX |
| `INSIGHTS_PRODUCT_PAGE_PLAYLISTS_TAB` | `insights_product_page_playlists_tab` | Product page playlists tab |
| `INSIGHTS_ARTIST_PLAYLISTS` | `insights_artist_playlists_tab` | Artist page playlists tab |
| `INSIGHTS_SONG_PLAYLISTS_PERFORMANCE` | `insights_song_playlists_performance` | Song playlists performance |
| `INSIGHTS_ARTIST_PLAYLISTS_PERFORMANCE` | `insights_artist_playlists_performance` | Artist playlists performance |
| `INSIGHTS_PRODUCT_PLAYLISTS_PERFORMANCE` | `insights_product_playlists_performance` | Product playlists performance |
| `INSIGHTS_POT_FILTERS_V3` | `insights_pot_filters_v3` | PoT filters v3 (product page) |
| `INSIGHTS_DAILY_EXPORT` | `insights_daily_export` | Daily export feature |

Note: `insights_playlist_page_hourly_playlists`, `insights_playlist_page_apple_music_playlists`, `insights_playlist_current_tracklist_preaggregated`, `insights_playlist_page_hide_compilation_art` are defined in ows-playlist and graphql-analytics — NOT in frontend-insights constants file.

## Apollo GraphQL Queries (frontend-insights)

Query files: `src/apollo/queries/playlists/`

- `playlistMetadata` — fetches playlist metadata for the page header
- `performanceOverTime` — PoT chart data

### playlistMetadata Query

Uses `usePlaylistMetadataQuery(params, skip)` hook.
Params: `{ storeId, storePlaylistId, storefront? }`

**Data source for header**: Both `PlaylistPageHeader` (identity fields) and `PlaylistPageHeaderMetrics` (metric fields) are sourced from **`V_PLAYLIST_METADATA`** via graphql-knowledge's `playlistMetadataById` DataLoader — NOT from `V_PLAYLISTS_BY_PLAYLIST`.

The header fields that come from `V_PLAYLIST_METADATA`:
- `PLAYLIST_FOLLOWER_COUNT` — follower count shown in header
- `PLAYLIST_TRACK_COUNT` — track count shown in header
- `PLAYLIST_TYPE` — the playlist type label (e.g. CURATED, EDITORIAL)
- `OWNER` — Sony / Universal / Spotify / etc.

**The three circular header metrics** (rendered as donut/percentage rings in `PlaylistPageHeaderMetrics`):
1. `FRONTLINE_PERCENT` — share of tracks that are frontline releases (VARIANT, can be NULL)
2. `SMG_PERCENT` — share of tracks that are SMG (VARIANT, can be NULL)
3. `LOCAL_PERCENT` — share of tracks whose ISRC country prefix matches the user's current context market (e.g. `USXXX` ISRCs are "local" for a US user) (VARIANT, can be NULL)

`PRIMARY_PLAYLIST_TYPE` is **not** one of the three circular metrics. It is a sub-categorization field used only to flag whether a playlist is designated "Hot Hits". It is separate from `PLAYLIST_TYPE` and largely irrelevant to header metric display.

If `FRONTLINE_PERCENT`, `SMG_PERCENT`, or `LOCAL_PERCENT` are NULL in `V_PLAYLIST_METADATA`, the corresponding circular metric will be blank in the UI. This happens when the dbt track-categorization models have not resolved values — typically because the playlist contains no Orchard-distributed (DDEX-registered) tracks that can be classified.

## Other Playlist-related Pages

- `src/pages/playlists/playlistsPage.tsx` — Playlists list/browse page
- `src/pages/song/pages/playlists/playlists.tsx` — Song's playlists tab
- `src/pages/product/pages/playlists/playlists.tsx` — Product's playlists tab
- `src/pages/participant/pages/playlists/playlists.tsx` — Artist/participant playlists tab
- `src/pages/searchES/pages/playlistResults/playlistResults.tsx` — Search results for playlists

## Key Recent Commits (frontend-insights)

- `ca7ef696d` — fix: conflicts
- `f3c5ab9fb` — `IN-16755`: Add storefront parameter to playlist page
- `b94501b54` — `IN-16696`: PoT Demographics — add stream countries filter
- `fbf92c1f0` — `IN-16696`: Updated endpoint for PoT demographics
- `56a53a473` — `IN-16465`: Amplitude events for playlist page
- `3476b1947` — `IN-16401`: Product page PoT filter updates
- `d2efc6b00` — `IN-16420`: Add Followers to Playlist PoT
- `1906ef996` — `IN-16696`: PoT Demographics initial implementation
- `666852726` — `IN-16729`: Pass storeId to getPlaylistMetadata/getPlaylistMetadataList
- `19778acfa` — `IN-16735`: Playlist page UI fixes per design
- `14ac5664e` — `IN-16646`: Song page daily export by countries
