Stay organized with collections
Save and categorize content based on your preferences.
A media app that runs on a TV needs to allow users to browse its content
offerings, make a selection, and start playing content. The content browsing
experience for apps of this type should be simple, intuitive, and visually
pleasing and engaging.
A media catalog browser tends to consist of several sections, and each section
has a list of media content. Examples of sections in a media catalog include:
playlists, featured content, recommended categories.
Figure 1. Typical catalog screen. Users are able to browse video catalog data.
Use the functions provided by Compose for TV to implement a user
interface for browsing music or videos from your app's media catalog.
Create a composable function for catalog
Everything appearing on a display is implemented as a composable function in
Compose for TV. Start by defining a composable
function for the media catalog browser:
CatalogBrowser is the composable function implementing your media catalog
browser. The function takes the following arguments:
List of featured content.
List of sections.
A Modifier object.
A callback function, which triggers a screen transition.
Set UI elements
Compose for TV offers lazy lists, a component to display a large
number of items (or a list of an unknown length). Call
LazyColumn
to place sections vertically. LazyColumn provides a
LazyListScope.() -> Unit
block, which offers a DSL to define item contents. In the following example,
each section is placed in a vertical list with a 16 dp gap between sections:
In the example, Section composable function defines how to display sections.
In the following function, LazyRow demonstrates how this
horizontal version of LazyColumn is similarly used to
define a horizontal list with a LazyListScope.() -> Unit block by calling
the provided DSL:
In the Section composable, the Text component is used.
Text and other components
defined in Material Design are offered in the tv-material library . You can
change the texts' style as defined in Material Design by referring to the
MaterialTheme object.
This object is also provided by the tv-material library.
Card is part of the tv-material library.
MovieCard defines how each movie data is rendered in the catalog defined
as the following snippet:
In the example described earlier, all movies are displayed equally.
They have the same area, no visual difference between them.
You can highlight some of them with Carousel.
Carousel displays the information in a set of items that can slide, fade, or
move into view. You use the component to highlight featured content, such as
newly available movies or new episodes of TV programs.
Carousel
expects you to at least specify the number of items that Carousel has and how to
draw each item. The first one can be specified with itemCount. The second one
can be passed as a lambda. The index number of the displayed item is
given to the lambda. You can determine the displayed item with the
given index value:
Carousel can be an item of a lazy list, such as TvLazyColumn.
The following snippet shows FeaturedCarousel composable on top of the
all Section composables:
Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
Last updated 2025-08-20 UTC.
[null,null,["Last updated 2025-08-20 UTC."],[],[],null,["# Create a catalog browser\n\nA media app that runs on a TV needs to allow users to browse its content\nofferings, make a selection, and start playing content. The content browsing\nexperience for apps of this type should be simple, intuitive, and visually\npleasing and engaging.\n\nA media catalog browser tends to consist of several sections, and each section\nhas a list of media content. Examples of sections in a media catalog include:\nplaylists, featured content, recommended categories.\n**Figure 1.** Typical catalog screen. Users are able to browse video catalog data.\n\nUse the functions provided by Compose for TV to implement a user\ninterface for browsing music or videos from your app's media catalog.\n\nCreate a composable function for catalog\n----------------------------------------\n\nEverything appearing on a display is implemented as a composable function in\nCompose for TV. Start by defining a composable\nfunction for the media catalog browser: \n\n @Composable\n fun CatalogBrowser(\n featuredContentList: List\u003cMovie\u003e,\n sectionList: List\u003cSection\u003e,\n modifier: Modifier = Modifier,\n onItemSelected: (Movie) -\u003e Unit = {},\n ) {\n // ToDo: add implementation\n }\n\n`CatalogBrowser` is the composable function implementing your media catalog\nbrowser. The function takes the following arguments:\n\n- List of featured content.\n- List of sections.\n- A Modifier object.\n- A callback function, which triggers a screen transition.\n\nSet UI elements\n---------------\n\nCompose for TV offers lazy lists, a component to display a large\nnumber of items (or a list of an unknown length). Call\n[`LazyColumn`](/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyColumn(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))\nto place sections vertically. `LazyColumn` provides a\n[`LazyListScope.() -\u003e Unit`](/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope)\nblock, which offers a DSL to define item contents. In the following example,\neach section is placed in a vertical list with a 16 dp gap between sections: \n\n @Composable\n fun CatalogBrowser(\n featuredContentList: List\u003cMovie\u003e,\n sectionList: List\u003cSection\u003e,\n modifier: Modifier = Modifier,\n onItemSelected: (Movie) -\u003e Unit = {},\n ) {\n LazyColumn(\n modifier = modifier.fillMaxSize(),\n verticalArrangement = Arrangement.spacedBy(16.dp)\n ) {\n items(sectionList) { section -\u003e\n Section(section, onItemSelected = onItemSelected)\n }\n }\n }\n\nIn the example, `Section` composable function defines how to display sections.\nIn the following function, [`LazyRow`](/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyRow(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.ui.Alignment.Vertical,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)) demonstrates how this\nhorizontal version of [`LazyColumn`](/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyColumn(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)) is similarly used to\ndefine a horizontal list with a `LazyListScope.() -\u003e Unit` block by calling\nthe provided DSL: \n\n @Composable\n fun Section(\n section: Section,\n modifier: Modifier = Modifier,\n onItemSelected: (Movie) -\u003e Unit = {},\n ) {\n Text(\n text = section.title,\n style = MaterialTheme.typography.headlineSmall,\n )\n LazyRow(\n modifier = modifier,\n horizontalArrangement = Arrangement.spacedBy(8.dp)\n ) {\n items(section.movieList){ movie -\u003e\n MovieCard(\n movie = movie,\n onClick = { onItemSelected(movie) }\n )\n }\n }\n }\n\nIn the `Section` composable, the [`Text`](/reference/kotlin/androidx/tv/material3/package-summary#Text(androidx.compose.ui.text.AnnotatedString,androidx.compose.ui.Modifier,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.font.FontStyle,androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontFamily,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.style.TextDecoration,androidx.compose.ui.text.style.TextAlign,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.style.TextOverflow,kotlin.Boolean,kotlin.Int,kotlin.collections.Map,kotlin.Function1,androidx.compose.ui.text.TextStyle)) component is used.\nText and other components\ndefined in Material Design are offered in the tv-material library . You can\nchange the texts' style as defined in Material Design by referring to the\n[`MaterialTheme`](/reference/kotlin/androidx/tv/material3/MaterialTheme) object.\nThis object is also provided by the tv-material library.\n[`Card`](/reference/kotlin/androidx/tv/material3/package-summary#Card(kotlin.Function0,androidx.compose.ui.Modifier,androidx.tv.material3.CardShape,androidx.tv.material3.CardColors,androidx.tv.material3.CardScale,androidx.tv.material3.CardBorder,androidx.tv.material3.CardGlow,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.Function1)) is part of the tv-material library.\n`MovieCard` defines how each movie data is rendered in the catalog defined\nas the following snippet: \n\n @Composable\n fun MovieCard(\n movie: Movie,\n modifier: Modifier = Modifier,\n onClick: () -\u003e Unit = {}\n ) {\n Card(modifier = modifier, onClick = onClick){\n AsyncImage(\n model = movie.thumbnailUrl,\n contentDescription = movie.title,\n )\n }\n }\n\n| **Note:** `AsyncImage` is a composable that loads an image from the internet. See [Loading Images](/develop/ui/compose/graphics/images/loading#internet-loading) for details.\n\nHighlight featured content\n--------------------------\n\nIn the example described earlier, all movies are displayed equally.\nThey have the same area, no visual difference between them.\nYou can highlight some of them with [`Carousel`](/reference/kotlin/androidx/tv/material3/package-summary#Carousel(kotlin.Int,androidx.compose.ui.Modifier,androidx.tv.material3.CarouselState,kotlin.Long,androidx.compose.animation.ContentTransform,androidx.compose.animation.ContentTransform,kotlin.Function1,kotlin.Function2)).\n\nCarousel displays the information in a set of items that can slide, fade, or\nmove into view. You use the component to highlight featured content, such as\nnewly available movies or new episodes of TV programs.\n\n[`Carousel`](/reference/kotlin/androidx/tv/material3/package-summary#Carousel(kotlin.Int,androidx.compose.ui.Modifier,androidx.tv.material3.CarouselState,kotlin.Long,androidx.compose.animation.ContentTransform,androidx.compose.animation.ContentTransform,kotlin.Function1,kotlin.Function2))\nexpects you to at least specify the number of items that Carousel has and how to\ndraw each item. The first one can be specified with `itemCount`. The second one\ncan be passed as a lambda. The index number of the displayed item is\ngiven to the lambda. You can determine the displayed item with the\ngiven index value: \n\n @Composable\n function FeaturedCarousel(\n featuredContentList: List\u003cMovie\u003e,\n modifier: Modifier = Modifier,\n ) {\n Carousel(\n itemCount = featuredContentList.size,\n modifier = modifier,\n ) { index -\u003e\n val content = featuredContentList[index]\n Box {\n AsyncImage(\n model = content.backgroundImageUrl,\n contentDescription = content.description,\n placeholder = painterResource(\n id = R.drawable.placeholder\n ),\n contentScale = ContentScale.Crop,\n modifier = Modifier.fillMaxSize()\n )\n Text(text = content.title)\n }\n }\n }\n\n`Carousel` can be an item of a lazy list, such as `TvLazyColumn`.\nThe following snippet shows `FeaturedCarousel` composable on top of the\nall `Section` composables: \n\n @Composable\n fun CatalogBrowser(\n featuredContentList: List\u003cMovie\u003e,\n sectionList: List\u003cSection\u003e,\n modifier: Modifier = Modifier,\n onItemSelected: (Movie) -\u003e Unit = {},\n ) {\n TvLazyColumn(\n modifier = modifier.fillMaxSize(),\n verticalArrangement = Arrangement.spacedBy(16.dp)\n ) {\n\n item {\n FeaturedCarousel(featuredContentList)\n }\n\n items(sectionList) { section -\u003e\n Section(section, onItemSelected = onItemSelected)\n }\n }\n }"]]