LazyColumn is a composable function in Jetpack Compose for creating vertical lists with lazy loading of elements. Unlike Column, LazyColumn creates and composes only the elements visible on screen, reusing them during scrolling. According to Google Android Documentation, 2025, LazyColumn is the primary tool for displaying large data sets in mobile applications thanks to efficient memory management.
Key Takeaways
LazyColumn is a composable function that implements a vertical scrollable list with lazy loading. Unlike Column, which renders all children at once, LazyColumn creates only elements that fall within the viewport.
The lazy loading mechanism in LazyColumn is based on element reuse: when an element is hidden during scrolling, its composition and layout can be reused for a new displayed element. This saves memory and CPU time, especially on long lists.
According to Android Developers Blog, 2024, LazyColumn can efficiently handle lists of tens of thousands of elements without significant performance degradation. Each element in LazyColumn has a key that helps Compose identify and preserve the element’s state when the list changes.
LazyColumn accepts parameters modifier, state, contentPadding, reverseLayout, verticalArrangement and horizontalAlignment. Content is defined in a lambda block using DSL functions items and item.
@Composable
fun SimpleLazyColumn() {
val itemsList = (1..100).toList()
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp)
) {
items(itemsList) { item ->
Text(text = "Item $item")
}
}
}
The LazyColumn DSL block provides items and item functions for adding content. items accepts a data collection, a key, and a factory function to create elements. item adds a single static element to the list.
itemsIndexed is a variant of items that passes the element index in addition to the data. This is useful when you need to know the element position without separate calculation. items(size) accepts only the number of elements and a factory without binding to a collection.
The key parameter in items uniquely identifies each element. According to Material Design Guidelines, 2025, keys improve performance when updating and animating elements, as Compose can track the movement or change of a specific element by key.
The items function with contentType allows LazyColumn to optimize reuse of elements of different types. The example below uses section headers and regular rows in a single list.
@Composable
fun MixedItemsList() {
LazyColumn {
item {
Text(
text = "Header",
style = MaterialTheme.typography.headlineMedium
)
}
items(
items = (1..20).toList(),
key = { it },
contentType = { "item" }
) { item ->
Text(text = "Row $item")
}
}
}
LazyListState is a LazyColumn state object that tracks the current scroll position, the first visible element, and the offset. It can be created via rememberLazyListState() and passed to the state parameter of LazyColumn.
LazyListState provides animateScrollToItem and scrollToItem methods for programmatic scrolling to a specific element. animateScrollToItem creates a smooth scroll animation, while scrollToItem scrolls instantly. Both methods require calling within a coroutine in CompositionContext.
According to Android Developers, 2024, LazyListState also contains layoutInfo, which provides information about visible elements — their indices, offsets, and sizes. This is useful for implementing analytics, view tracking, or infinite scrolling.
In this example, LazyColumn is controlled via LazyListState. A button scrolls the list to item with index 50 with a smooth animation.
@Composable
fun ScrollableList() {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
Column {
Button(
onClick = {
scope.launch {
listState.animateScrollToItem(50)
}
}
) {
Text("Scroll to 50")
}
LazyColumn(state = listState) {
items((1..100).toList()) { item ->
Text(
text = "Item $item",
modifier = Modifier.padding(8.dp)
)
}
}
}
}
The key performance factor of LazyColumn is proper use of keys. Each element should have a unique stable key via the key parameter. Without keys, Compose cannot correctly reuse elements when data changes, leading to recomposition.
contentType tells LazyColumn which elements have the same layout type. When two elements with the same contentType enter the recycling pool, LazyColumn can reuse their layout without recomposition. According to Android Developers Blog, 2024, contentType improves performance by 30–50% in lists with different element types.
For LazyColumn, it is also important to avoid unstable parameters in factory functions. Each time an element parameter changes, Compose recomposes that element. Using remember and derivedStateOf for stable values reduces the number of recompositions.
| Factor | Impact | Recommendation |
|---|---|---|
| Keys (key) | Critical | Always set a unique stable key for items |
| contentType | Significant | Specify the element type to optimize reuse |
| Element size | Moderate | Use fixed sizes where possible |
| Nested lists | Critical | Avoid LazyColumn inside LazyColumn — use LazyVerticalGrid |
stickyHeader is a LazyColumn DSL function that pins a section header to the top of the list during scrolling. When the next section reaches the top, its header replaces the current one. This behavior is familiar to users from contact lists and calendars.
stickyHeader accepts a key and a factory function, similar to item. Unlike a regular item, stickyHeader remains visible while scrolling through its section’s content. When a section is fully scrolled, the next section’s header pushes out the previous one.
According to Material Design Guidelines, 2025, sticky headers improve navigation in long lists and reduce cognitive load on the user. To implement a sticky header, simply wrap a section header in stickyHeader at the correct position relative to the items elements.
@Composable
fun SectionedList(sections: Map<String, List<String>>) {
LazyColumn {
sections.forEach { (header, items) ->
stickyHeader {
Text(
text = header,
modifier = Modifier.background(Color.White)
.padding(16.dp)
)
}
items(items) { item ->
Text(text = item, modifier = Modifier.padding(8.dp))
}
}
}
}
Frequently Asked Questions
Column renders all elements at once and is suitable for lists up to 20–30 elements. LazyColumn creates only visible elements and reuses them during scrolling, allowing you to work with thousands of elements without performance loss.
Use Arrangement.spacedBy in the verticalArrangement parameter or items functions with a separator via index. For complex separators, add a separate item with Divider() between list elements.
LazyColumn automatically recomposes when the data collection changes, if stable keys are used. Data changes detected via mutableStateListOf or StateFlow cause only the changed elements to redraw.
Direct nesting of LazyColumn inside LazyColumn is not recommended — both have independent scrolling, creating gesture conflicts. Instead, use a regular Column with LazyColumn inside a scrollable Section or LazyVerticalGrid for grids.
Use LazyListState.layoutInfo.visibleItemsInfo and compare the index of the last visible item with the total number of items. For infinite scrolling, track when the last visible item approaches 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