LazyRow — is a composable function in Jetpack Compose for creating horizontal lists with lazy loading of elements. Unlike Row, LazyRow creates only visible elements and reuses them during horizontal scrolling. According to Google Android Documentation, 2025, LazyRow is the horizontal counterpart of LazyColumn and is effective for carousels, galleries and horizontal menus in mobile applications.
Key Takeaways
LazyRow — is a composable function implementing a horizontal scrollable list with lazy loading of elements. Unlike Row, which renders all child elements simultaneously, LazyRow creates only elements visible in the viewport.
LazyRow works on the same principle as LazyColumn, but arranges elements horizontally. When scrolling left or right, elements that exit the viewport are reused to display new ones. This is critically important for galleries with many images or long category lists.
According to Android Developers Blog, 2024, LazyRow uses the same LazyList architecture as LazyColumn, including LazyListState, a reuse mechanism via Pool, and support for different element types. Maximum efficiency is achieved with fixed element height.
LazyRow accepts parameters modifier, state, contentPadding, reverseLayout, horizontalArrangement and verticalAlignment. Content is defined via DSL functions items and item, similar to LazyColumn.
@Composable
fun SimpleLazyRow() {
val items = (1..50).toList()
LazyRow(
modifier = Modifier.fillMaxWidth().height(100.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(start = 16.dp, end = 16.dp)
) {
items(items) { item ->
Box(
modifier = Modifier.size(80.dp).background(Color.Gray)
) {
Text(text = "$item", modifier = Modifier.align(Alignment.Center))
}
}
}
}
horizontalArrangement manages the distribution of elements along the horizontal axis inside LazyRow. The same Arrangement values are available as in Row: Start, Center, End, SpaceBetween, SpaceEvenly, SpaceAround and spacedBy with custom spacing.
contentPadding adds padding at the edges of the LazyRow container. Unlike Modifier.padding, contentPadding is taken into account during scrolling: the first element will have padding from the left edge, the last — from the right edge. According to Material Design Guidelines, 2025, contentPadding is required for carousels and galleries so that the first and last elements do not stick to the screen edge.
verticalAlignment aligns elements vertically inside LazyRow. By default, Alignment.Top is used. To center elements of different heights, use Alignment.CenterVertically, which is useful for carousels with images of different aspect ratios.
| Parameter | Description | Default value |
|---|---|---|
| horizontalArrangement | Horizontal distribution of elements | Arrangement.Start |
| contentPadding | Padding at container edges | PaddingValues(0.dp) |
| verticalAlignment | Vertical alignment | Alignment.Top |
| reverseLayout | Reverse element order | false |
| userScrollEnabled | Enable user scrolling | true |
LazyListState for LazyRow works identically to LazyColumn: it tracks the first visible element, offset and overall scroll position. The state is created via rememberLazyListState() and passed to the state parameter.
Programmatic scroll control of LazyRow is done with the same methods: animateScrollToItem for smooth animation and scrollToItem for instant scrolling. Both are called inside a coroutine. This is useful for carousels with "Left/Right" buttons or for automatic scrolling.
According to Android Developers, 2024, LazyRow also supports SnapFlingBehavior — behavior for fast scrolling that stops at the nearest element rather than between elements. This creates a "snap" effect familiar to carousels and galleries.
@Composable
fun CarouselWithSnap() {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
val items = listOf("A", "B", "C", "D", "E")
Column {
LazyRow(
state = listState,
flingBehavior = SnapFlingBehavior(listState),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(items) { item ->
Card(modifier = Modifier.width(150.dp)) {
Text(text = item, modifier = Modifier.padding(16.dp))
}
}
}
Row(modifier = Modifier.fillMaxWidth()) {
Button(onClick = {
scope.launch {
listState.animateScrollToItem(0)
}
}) { Text("To start") }
}
}
}
The first example — LazyRow for an image gallery with previews. Each element is a Box with an image and a caption. LazyRow efficiently handles dozens and hundreds of images, loading only visible ones.
@Composable
fun ImageGallery(images: List<String>) {
LazyRow(
contentPadding = PaddingValues(start = 16.dp, end = 16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
items(images, key = { it }) { url ->
AsyncImage(
model = url,
contentDescription = "Image",
modifier = Modifier.size(120.dp)
)
}
}
}
The second example — LazyRow for a category list with icons. Categories are displayed as chips, and the user can scroll them horizontally. LazyRow with spacedBy and contentPadding creates spacing between categories and at the edges.
@Composable
fun CategoryChips(categories: List<String>) {
LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(categories, key = { it }) { category ->
AssistChip(
onClick = {},
label = { Text(category) }
)
}
}
}
The choice between LazyRow and Row depends on the number of elements. Row is suitable for 5–10 elements that are always visible on screen. LazyRow is essential when there are many elements or their count is dynamic.
Row renders all elements at once, causing delays with a large number of elements. LazyRow renders only visible ones, making it preferable for scrollable lists. According to Android Developers Blog, 2024, the threshold is approximately 10 elements: if there are more elements, use LazyRow.
LazyRow also supports animations, SnapFlingBehavior and grouping via separators, making it more flexible for complex interfaces. Row is simpler and does not require LazyListState, but is not optimized for long lists.
Frequently Asked Questions
LazyRow scrolls horizontally and arranges elements left to right, while LazyColumn scrolls vertically top to bottom. Both use the same principles of lazy loading, LazyListState and DSL items functions.
Pass SnapFlingBehavior(listState) to the flingBehavior parameter of LazyRow. SnapFlingBehavior forces LazyRow to stop at the nearest element rather than between elements, creating a snapping effect for carousels.
Track the last visible element via LazyListState.layoutInfo and load new data when the last element approaches the end of the list. For circular scrolling, use a custom implementation with element duplication.
Yes, LazyRow can be nested inside LazyColumn. This is a standard pattern for creating complex pages with horizontal carousels inside a vertical list. Each LazyRow must have a fixed height for proper operation.
Use LazyListState.layoutInfo.visibleItemsInfo and compare the index of the last visible element with the total count minus one. When these values match, LazyRow is at the end of the list.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also