Many apps need to display collections of items. This document explains how you can efficiently do this in Jetpack Compose.

If you know that your use case does not require any scrolling, you may wish to use a simple[`Column`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,kotlin.Function1))or[`Row`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Row(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.ui.Alignment.Vertical,kotlin.Function1))(depending on the direction), and emit each item's content by iterating over a list in the following way:

<br />

```kotlin
@Composable
fun MessageList(messages: List<Message>) {
    Column {
        messages.forEach { message ->
            MessageRow(message)
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L88-L95
```

<br />

We can make the[`Column`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,kotlin.Function1))scrollable by using the`verticalScroll()`modifier.

## Lazy lists

If you need to display a large number of items (or a list of an unknown length), using a layout such as[`Column`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,kotlin.Function1))can cause performance issues, since all the items will be composed and laid out whether or not they are visible.

Compose provides a set of components which only compose and lay out items which are visible in the component's viewport. These components include[`LazyColumn`](https://developer.android.com/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))and[`LazyRow`](https://developer.android.com/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)).
| **Note:** If you've used the[`RecyclerView`widget](https://developer.android.com/guide/topics/ui/layout/recyclerview), these components follow the same set of principles.

As the name suggests, the difference between[`LazyColumn`](https://developer.android.com/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))and[`LazyRow`](https://developer.android.com/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))is the orientation in which they lay out their items and scroll.[`LazyColumn`](https://developer.android.com/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))produces a vertically scrolling list, and[`LazyRow`](https://developer.android.com/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))produces a horizontally scrolling list.

The Lazy components are different to most layouts in Compose. Instead of accepting a`@Composable`content block parameter, allowing apps to directly emit composables, the Lazy components provide a`LazyListScope.()`block. This[`LazyListScope`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope)block offers a DSL which allows apps to*describe*the item contents. The Lazy component is then responsible for adding the each item's content as required by the layout and scroll position.
| **Key Term:** *DSL* stands for**domain-specific language** . See the[Kotlin for Compose](https://developer.android.com/develop/ui/compose/kotlin#dsl)documentation for more information on how Compose defines DSLs for some APIs.

## `LazyListScope`DSL

The DSL of[`LazyListScope`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope)provides a number of functions for describing items in the layout. At the most basic,[`item()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope#item(kotlin.Any,kotlin.Any,kotlin.Function1))adds a single item, and[`items(Int)`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope#items(kotlin.Int,kotlin.Function1,kotlin.Function1,kotlin.Function2))adds multiple items:

<br />

```kotlin
LazyColumn {
    // Add a single item
    item {
        Text(text = "First item")
    }

    // Add 5 items
    items(5) { index ->
        Text(text = "Item: $index")
    }

    // Add another single item
    item {
        Text(text = "Last item")
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L102-L117
```

<br />

There are also a number of extension functions which allow you to add collections of items, such as a`List`. These extensions allow us to easily migrate our[`Column`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,kotlin.Function1))example from above:

<br />

```kotlin
/**
 * import androidx.compose.foundation.lazy.items
 */
LazyColumn {
    items(messages) { message ->
        MessageRow(message)
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L125-L132
```

<br />

There is also a variant of the[`items()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope#items(kotlin.Int,kotlin.Function1,kotlin.Function1,kotlin.Function2))extension function called[`itemsIndexed()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#(androidx.compose.foundation.lazy.LazyListScope).itemsIndexed(kotlin.collections.List,kotlin.Function2,kotlin.Function2,kotlin.Function3)), which provides the index. Please see the[`LazyListScope`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope)reference for more details.

## Lazy grids

The[`LazyVerticalGrid`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/package-summary#LazyVerticalGrid(androidx.compose.foundation.lazy.grid.GridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.grid.LazyGridState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))and[`LazyHorizontalGrid`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/package-summary#LazyHorizontalGrid(androidx.compose.foundation.lazy.grid.GridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.grid.LazyGridState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))composables provide support for displaying items in a grid. A Lazy vertical grid will display its items in a vertically scrollable container, spanned across multiple columns, while the Lazy horizontal grids will have the same behaviour on the horizontal axis.

Grids have the same powerful API capabilities as lists and they also use a very similar DSL -[`LazyGridScope.()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScope)for describing the content.

![Screenshot of a phone showing a grid of photos](https://developer.android.com/static/develop/ui/compose/images/lists-photogrid.png)

The`columns`parameter in[`LazyVerticalGrid`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/package-summary#LazyVerticalGrid(androidx.compose.foundation.lazy.grid.GridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.grid.LazyGridState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))and`rows`parameter in[`LazyHorizontalGrid`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/package-summary#LazyHorizontalGrid(androidx.compose.foundation.lazy.grid.GridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.grid.LazyGridState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))control how cells are formed into columns or rows. The following example displays items in a grid, using[`GridCells.Adaptive`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/GridCells.Adaptive)to set each column to be at least`128.dp`wide:

<br />

```kotlin
LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 128.dp)
) {
    items(photos) { photo ->
        PhotoItem(photo)
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L249-L255
```

<br />

`LazyVerticalGrid`lets you specify a width for items, and then the grid will fit as many columns as possible. Any remaining width is distributed equally among the columns, after the number of columns is calculated. This adaptive way of sizing is especially useful for displaying sets of items across different screen sizes.

If you know the exact number of columns to be used, you can instead provide an instance of[`GridCells.Fixed`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/GridCells.Fixed)containing the number of required columns.

If your design requires only certain items to have non-standard dimensions, you can use the grid support for providing custom column spans for items. Specify the column span with the`span`parameter of the[`LazyGridScope DSL`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScope)`item`and`items`methods.[`maxLineSpan`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemSpanScope#maxLineSpan()), one of the span scope's values, is particularly useful when you're using adaptive sizing, because the number of columns is not fixed. This example shows how to provide a full row span:

<br />

```kotlin
LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 30.dp)
) {
    item(span = {
        // LazyGridItemSpanScope:
        // maxLineSpan
        GridItemSpan(maxLineSpan)
    }) {
        CategoryCard("Fruits")
    }
    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L139-L150
```

<br />

## Lazy staggered grid

[`LazyVerticalStaggeredGrid`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyVerticalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.ui.unit.Dp,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))and[`LazyHorizontalStaggeredGrid`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyHorizontalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.unit.Dp,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))are composables that allow you to create a lazy-loaded, staggered grid of items. A lazy vertical staggered grid displays its items in a vertically scrollable container that spans across multiple columns and allows individual items to be different heights. Lazy horizontal grids have the same behavior on the horizontal axis with items of different widths.
| **Note:** The difference between a lazy staggered grid and a lazy grid is that the former can display items of different size widths or heights.

The following snippet is a basic example of using`LazyVerticalStaggeredGrid`with a`200.dp`width per item:

<br />

```kotlin
LazyVerticalStaggeredGrid(
    columns = StaggeredGridCells.Adaptive(200.dp),
    verticalItemSpacing = 4.dp,
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    content = {
        items(randomSizedPhotos) { photo ->
            AsyncImage(
                model = photo,
                contentScale = ContentScale.Crop,
                contentDescription = null,
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
            )
        }
    },
    modifier = Modifier.fillMaxSize()
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L647-L664
```

<br />

Figure 1. Example of lazy staggered vertical grid

To set a fixed number of columns, you can use`StaggeredGridCells.Fixed(columns)`instead of`StaggeredGridCells.Adaptive`. This divides the available width by the number of columns (or rows for a horizontal grid), and has each item take up that width (or height for a horizontal grid):

<br />

```kotlin
LazyVerticalStaggeredGrid(
    columns = StaggeredGridCells.Fixed(3),
    verticalItemSpacing = 4.dp,
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    content = {
        items(randomSizedPhotos) { photo ->
            AsyncImage(
                model = photo,
                contentScale = ContentScale.Crop,
                contentDescription = null,
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
            )
        }
    },
    modifier = Modifier.fillMaxSize()
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L671-L688
```
![Lazy staggered grid of images in Compose](https://developer.android.com/static/develop/ui/compose/images/lists/staggered_grid_fixed.png)Figure 2. Example of lazy staggered vertical grid with fixed columns

<br />

## Content padding

Sometimes you'll need to add padding around the edges of the content. The lazy components allow you to pass some[`PaddingValues`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/PaddingValues)to the`contentPadding`parameter to support this:

<br />

```kotlin
LazyColumn(
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
) {
    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L159-L163
```

<br />

In this example, we add`16.dp`of padding to the horizontal edges (left and right), and then`8.dp`to the top and bottom of the content.

Please note that this padding is applied to the*content* , not to the`LazyColumn`itself. In the example above, the first item will add`8.dp`padding to it's top, the last item will add`8.dp`to its bottom, and all items will have`16.dp`padding on the left and the right.

As another example, you can pass`Scaffold`'s`PaddingValues`into`LazyColumn`'s`contentPadding`. See the[edge-to-edge](https://developer.android.com/develop/ui/compose/layouts/insets#scaffold)guide.

## Content spacing

To add spacing in-between items, you can use[`Arrangement.spacedBy()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement#spacedBy(androidx.compose.ui.unit.Dp)). The example below adds`4.dp`of space in-between each item:

<br />

```kotlin
LazyColumn(
    verticalArrangement = Arrangement.spacedBy(4.dp),
) {
    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L170-L174
```

<br />

Similarly for[`LazyRow`](https://developer.android.com/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)):

<br />

```kotlin
LazyRow(
    horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L181-L185
```

<br />

Grids, however, accept both vertical and horizontal arrangements:

<br />

```kotlin
LazyVerticalGrid(
    columns = GridCells.Fixed(2),
    verticalArrangement = Arrangement.spacedBy(16.dp),
    horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
    items(photos) { item ->
        PhotoItem(item)
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L192-L200
```

<br />

## Item keys

By default, each item's state is keyed against the position of the item in the list or grid. However, this can cause issues if the data set changes, since items which change position effectively lose any remembered state. If you imagine the scenario of`LazyRow`within a`LazyColumn`, if the row changes item position, the user would then lose their scroll position within the row.
| **Note:** For more information on how Compose remembers state, see the[State](https://developer.android.com/develop/ui/compose/state)documentation

To combat this, you can provide a stable and unique key for each item, providing a block to the`key`parameter. Providing a stable key enables item state to be consistent across data-set changes:

<br />

```kotlin
LazyColumn {
    items(
        items = messages,
        key = { message ->
            // Return a stable + unique key for the item
            message.id
        }
    ) { message ->
        MessageRow(message)
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L375-L385
```

<br />

By providing keys, you help Compose to handle reorderings correctly. For example, if your item contains remembered state, setting keys would allow Compose to move this state together with the item, when its position changes.

<br />

```kotlin
LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = remember {
            Random.nextInt()
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L396-L402
```

<br />

However, there is one limitation on what types you can use as item keys. The key's type must be supported by[`Bundle`](https://developer.android.com/reference/android/os/Bundle), Android's mechanism for keeping the states when the Activity is recreated.`Bundle`supports types like primitives, enums or Parcelables.

<br />

```kotlin
LazyColumn {
    items(books, key = {
        // primitives, enums, Parcelable, etc.
    }) {
        // ...
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L412-L418
```

<br />

The key must be supported by`Bundle`so that the`rememberSaveable`inside the item composable can be restored when the Activity is recreated, or even when you scroll away from this item and scroll back.

<br />

```kotlin
LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = rememberSaveable {
            Random.nextInt()
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L428-L434
```

<br />

## Item animations

If you've used the RecyclerView widget, you'll know that it[animates item changes](https://developer.android.com/guide/topics/ui/layout/recyclerview-custom#animations)automatically. Lazy layouts provide the same functionality for item reorderings. The API is simple - you just need to set the[`animateItem`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyItemScope#(androidx.compose.ui.Modifier).animateItem(androidx.compose.animation.core.FiniteAnimationSpec,androidx.compose.animation.core.FiniteAnimationSpec,androidx.compose.animation.core.FiniteAnimationSpec))modifier to the item content:

<br />

```kotlin
LazyColumn {
    // It is important to provide a key to each item to ensure animateItem() works as expected.
    items(books, key = { it.id }) {
        Row(Modifier.animateItem()) {
            // ...
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L444-L451
```

<br />

You can even provide custom animation specification, if you need to:

<br />

```kotlin
LazyColumn {
    items(books, key = { it.id }) {
        Row(
            Modifier.animateItem(
                fadeInSpec = tween(durationMillis = 250),
                fadeOutSpec = tween(durationMillis = 100),
                placementSpec = spring(stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy)
            )
        ) {
            // ...
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L461-L473
```

<br />

Make sure you provide keys for your items so it is possible to find the new position for the moved element.

### Example: Animate items in lazy lists

With Compose, you can animate changes to items in lazy lists. When used together, the following snippets implement animations when adding, removing, and reordering lazy list items.

This snippet displays a list of strings with animated transitions when items are added, removed, or reordered:

<br />

```kotlin
@Composable
fun ListAnimatedItems(
    items: List<String>,
    modifier: Modifier = Modifier
) {
    LazyColumn(modifier) {
        // Use a unique key per item, so that animations work as expected.
        items(items, key = { it }) {
            ListItem(
                headlineContent = { Text(it) },
                modifier = Modifier
                    .animateItem(
                        // Optionally add custom animation specs
                    )
                    .fillParentMaxWidth()
                    .padding(horizontal = 8.dp, vertical = 0.dp),
            )
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/AnimatedOrderedList.kt#L96-L115
```

<br />

#### Key points about the code

- `ListAnimatedItems`displays a list of strings in a[`LazyColumn`](https://developer.android.com/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,androidx.compose.foundation.OverscrollEffect,kotlin.Function1))with animated transitions when items are modified.
- The`items`function assigns a unique key to each item in the list. Compose uses the keys to track the items and identify changes in their positions.
- `ListItem`defines the layout of each list item. It takes a`headlineContent`parameter, which defines the main content of the item.
- The[`animateItem`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyItemScope#(androidx.compose.ui.Modifier).animateItem(androidx.compose.animation.core.FiniteAnimationSpec,androidx.compose.animation.core.FiniteAnimationSpec,androidx.compose.animation.core.FiniteAnimationSpec))modifier applies default animations to item additions, removals, and moves.

The following snippet presents a screen that incorporates controls for adding and removing items, as well as sorting a predefined list:

<br />

```kotlin
@Composable
private fun ListAnimatedItemsExample(
    data: List<String>,
    modifier: Modifier = Modifier,
    onAddItem: () -> Unit = {},
    onRemoveItem: () -> Unit = {},
    resetOrder: () -> Unit = {},
    onSortAlphabetically: () -> Unit = {},
    onSortByLength: () -> Unit = {},
) {
    val canAddItem = data.size < 10
    val canRemoveItem = data.isNotEmpty()

    Scaffold(modifier) { paddingValues ->
        Column(
            modifier = Modifier
                .padding(paddingValues)
                .fillMaxSize()
        ) {
            // Buttons that change the value of displayedItems.
            AddRemoveButtons(canAddItem, canRemoveItem, onAddItem, onRemoveItem)
            OrderButtons(resetOrder, onSortAlphabetically, onSortByLength)

            // List that displays the values of displayedItems.
            ListAnimatedItems(data)
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/AnimatedOrderedList.kt#L119-L146
```

<br />

#### Key points about the code

- `ListAnimatedItemsExample`presents a screen that incorporates controls for adding, removing, and sorting items.
  - `onAddItem`and`onRemoveItem`are lambda expressions that are passed to`AddRemoveButtons`to add and remove items from the list.
  - `resetOrder`,`onSortAlphabetically`, and`onSortByLength`are lambda expressions that are passed to`OrderButtons`to change the order of the items in the list.
- `AddRemoveButtons`displays the "Add" and "Remove" buttons. It enables/disables the buttons and handles button clicks.
- `OrderButtons`displays the buttons for reordering the list. It receives the lambda functions for resetting the order and sorting the list by length or alphabetically.
- `ListAnimatedItems`calls the`ListAnimatedItems`composable, passing the`data`list to display the animated list of strings.`data`is defined elsewhere.

This snippet creates a UI with**Add Item** and**Delete Item**buttons:

<br />

```kotlin
@Composable
private fun AddRemoveButtons(
    canAddItem: Boolean,
    canRemoveItem: Boolean,
    onAddItem: () -> Unit,
    onRemoveItem: () -> Unit
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center
    ) {
        Button(enabled = canAddItem, onClick = onAddItem) {
            Text("Add Item")
        }
        Spacer(modifier = Modifier.padding(25.dp))
        Button(enabled = canRemoveItem, onClick = onRemoveItem) {
            Text("Delete Item")
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/AnimatedOrderedList.kt#L150-L169
```

<br />

#### Key points about the code

- `AddRemoveButtons`displays a row of buttons to perform add and remove operations on the list.
- The`canAddItem`and`canRemoveItem`parameters control the enabled state of the buttons. If`canAddItem`or`canRemoveItem`are false, then the corresponding button is disabled.
- The`onAddItem`and`onRemoveItem`parameters are lambdas that execute when the user clicks the corresponding button.

Finally, this snippet displays three buttons for sorting the list (**Reset, Alphabetical** , and**Length**):

<br />

```kotlin
@Composable
private fun OrderButtons(
    resetOrder: () -> Unit,
    orderAlphabetically: () -> Unit,
    orderByLength: () -> Unit
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center
    ) {
        var selectedIndex by remember { mutableIntStateOf(0) }
        val options = listOf("Reset", "Alphabetical", "Length")

        SingleChoiceSegmentedButtonRow {
            options.forEachIndexed { index, label ->
                SegmentedButton(
                    shape = SegmentedButtonDefaults.itemShape(
                        index = index,
                        count = options.size
                    ),
                    onClick = {
                        Log.d("AnimatedOrderedList", "selectedIndex: $selectedIndex")
                        selectedIndex = index
                        when (options[selectedIndex]) {
                            "Reset" -> resetOrder()
                            "Alphabetical" -> orderAlphabetically()
                            "Length" -> orderByLength()
                        }
                    },
                    selected = index == selectedIndex
                ) {
                    Text(label)
                }
            }
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/AnimatedOrderedList.kt#L173-L209
```

<br />

#### Key points about the code

- `OrderButtons`displays a`SingleChoiceSegmentedButtonRow`to allow users to select a sorting method on the list or reset the list order. A[`SegmentedButton`](https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#(androidx.compose.material3.(androidx.compose.material3.MultiChoiceSegmentedButtonRowScope).SegmentedButton(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.graphics.Shape,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.compose.material3.SegmentedButtonColors,androidx.compose.foundation.BorderStroke,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.Function0,kotlin.Function0))component lets you select a single option from a list of options.
- `resetOrder`,`orderAlphabetically`, and`orderByLength`are lambda functions that are executed when the corresponding button is selected.
- The`selectedIndex`state variable keeps track of the selected option.

#### Result

This video shows the result of the preceding snippets when items are reordered:

<br />

**Figure 1**. A list that animates item transitions when items are added, removed, or sorted.

<br />

## Sticky headers (experimental)

| **Caution:** Experimental APIs can change in the future or may be removed entirely.

The 'sticky header' pattern is helpful when displaying lists of grouped data. Below you can see an example of a 'contacts list', grouped by each contact's initial:

![Video of a phone scrolling up and down through a contacts list](https://developer.android.com/static/develop/ui/compose/images/lists-scrolling.gif)

To achieve a sticky header with`LazyColumn`, you can use the experimental[`stickyHeader()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope#stickyHeader(kotlin.Any,kotlin.Any,kotlin.Function1))function, providing the header content:

<br />

```kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ListWithHeader(items: List<Item>) {
    LazyColumn {
        stickyHeader {
            Header()
        }

        items(items) { item ->
            ItemRow(item)
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L206-L218
```

<br />

To achieve a list with multiple headers, like the 'contacts list' example above, you could do:

<br />

```kotlin
// This ideally would be done in the ViewModel
val grouped = contacts.groupBy { it.firstName[0] }

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ContactsList(grouped: Map<Char, List<Contact>>) {
    LazyColumn {
        grouped.forEach { (initial, contactsForInitial) ->
            stickyHeader {
                CharacterHeader(initial)
            }

            items(contactsForInitial) { contact ->
                ContactListItem(contact)
            }
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L224-L241
```

<br />

## Reacting to scroll position

Many apps need to react and listen to scroll position and item layout changes. The Lazy components support this use-case by hoisting the[`LazyListState`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState):

<br />

```kotlin
@Composable
fun MessageList(messages: List<Message>) {
    // Remember our own LazyListState
    val listState = rememberLazyListState()

    // Provide it to LazyColumn
    LazyColumn(state = listState) {
        // ...
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L262-L271
```

<br />

For simple use-cases, apps commonly only need to know information about the first visible item. For this[`LazyListState`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState)provides the[`firstVisibleItemIndex`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#firstVisibleItemIndex())and[`firstVisibleItemScrollOffset`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#firstVisibleItemScrollOffset())properties.

If we use the example of a showing and hiding a button based on if the user has scrolled past the first item:

<br />

```kotlin
@Composable
fun MessageList(messages: List<Message>) {
    Box {
        val listState = rememberLazyListState()

        LazyColumn(state = listState) {
            // ...
        }

        // Show the button if the first visible item is past
        // the first item. We use a remembered derived state to
        // minimize unnecessary compositions
        val showButton by remember {
            derivedStateOf {
                listState.firstVisibleItemIndex > 0
            }
        }

        AnimatedVisibility(visible = showButton) {
            ScrollToTopButton()
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L277-L299
```

<br />

| **Note:** The example above uses[`derivedStateOf()`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#derivedStateOf(kotlin.Function0))to minimize unnecessary compositions. For more information, see the[Side Effects](https://developer.android.com/develop/ui/compose/side-effects#derivedstateof)documentation.

Reading the state directly in composition is useful when you need to update other UI composables, but there are also scenarios where the event does not need to be handled in the same composition. A common example of this is sending an analytics event once the user has scrolled past a certain point. To handle this efficiently, we can use a[`snapshotFlow()`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#snapshotFlow(kotlin.Function0)):

<br />

```kotlin
val listState = rememberLazyListState()

LazyColumn(state = listState) {
    // ...
}

LaunchedEffect(listState) {
    snapshotFlow { listState.firstVisibleItemIndex }
        .map { index -> index > 0 }
        .distinctUntilChanged()
        .filter { it }
        .collect {
            MyAnalyticsService.sendScrolledPastFirstItemEvent()
        }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L306-L320
```

<br />

`LazyListState`also provides information about all of the items currently being displayed and their bounds on screen, via the[`layoutInfo`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#layoutInfo())property. See the[`LazyListLayoutInfo`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfo)class for more information.

## Controlling the scroll position

As well as reacting to scroll position, it's also useful for apps to be able to control the scroll position too.[`LazyListState`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState)supports this via the[`scrollToItem()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#scrollToItem(kotlin.Int,kotlin.Int))function, which 'immediately' snaps the scroll position, and[`animateScrollToItem()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#animateScrollToItem(kotlin.Int,kotlin.Int))which scrolls using an animation (also known as a smooth scroll):
| **Note:** Both[`scrollToItem()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#scrollToItem(kotlin.Int,kotlin.Int))and[`animateScrollToItem()`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState#animateScrollToItem(kotlin.Int,kotlin.Int))are suspending functions, which means that we need to invoke them in a coroutine. See our[coroutines documentation](https://developer.android.com/develop/ui/compose/kotlin#coroutines)for more information on how to do that in Compose.

<br />

```kotlin
@Composable
fun MessageList(messages: List<Message>) {
    val listState = rememberLazyListState()
    // Remember a CoroutineScope to be able to launch
    val coroutineScope = rememberCoroutineScope()

    LazyColumn(state = listState) {
        // ...
    }

    ScrollToTopButton(
        onClick = {
            coroutineScope.launch {
                // Animate scroll to the first item
                listState.animateScrollToItem(index = 0)
            }
        }
    )
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L326-L344
```

<br />

## Large data-sets (paging)

The[Paging library](https://developer.android.com/topic/libraries/architecture/paging/v3-overview)enables apps to support large lists of items, loading and displaying small chunks of the list as necessary. Paging 3.0 and later provides Compose support through the`androidx.paging:paging-compose`library.
| **Note:** Compose support is provided only for Paging 3.0 and later. If you're using an earlier version of the Paging library, you need to[migrate to 3.0](https://developer.android.com/topic/libraries/architecture/paging/v3-migration)first.

To display a list of paged content, we can use the[`collectAsLazyPagingItems()`](https://developer.android.com/reference/kotlin/androidx/paging/compose/package-summary#collectaslazypagingitems)extension function, and then pass in the returned[`LazyPagingItems`](https://developer.android.com/reference/kotlin/androidx/paging/compose/LazyPagingItems)to`items()`in our`LazyColumn`. Similar to Paging support in views, you can display placeholders while data loads by checking if the`item`is`null`:

<br />

```kotlin
@Composable
fun MessageList(pager: Pager<Int, Message>) {
    val lazyPagingItems = pager.flow.collectAsLazyPagingItems()

    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it.id }
        ) { index ->
            val message = lazyPagingItems[index]
            if (message != null) {
                MessageRow(message)
            } else {
                MessagePlaceholder()
            }
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L350-L367
```

<br />

| **Warning:** If you use a[`RemoteMediator`](https://developer.android.com/reference/kotlin/androidx/paging/RemoteMediator)to fetch data from a network service, make sure to provide realistically sized placeholder items. If you use a`RemoteMediator`, it will be repeatedly invoked to fetch new data, up until the screen has been filled with content. If small placeholders are provided (or no placeholder at all), the screen might never be filled, and your app will fetch many pages of data.

## Tips on using Lazy layouts

There are a few tips you can take into account to ensure your Lazy layouts work as intended.

### Avoid using 0-pixel sized items

This can happen in scenarios where, for example, you expect to asynchronously retrieve some data like images, to fill your list's items at a later stage. That would cause the Lazy layout to compose all of its items in the first measurement, as their height is 0 pixels and it could fit them all in the viewport. Once the items have loaded and their height expanded, Lazy layouts would then discard all of the other items that have unnecessarily been composed the first time around as they cannot in fact fit the viewport. To avoid this, you should set default sizing to your items, so that the Lazy layout can do the correct calculation of how many items can in fact fit in the viewport:

<br />

```kotlin
@Composable
fun Item(imageUrl: String) {
    AsyncImage(
        model = rememberAsyncImagePainter(model = imageUrl),
        modifier = Modifier.size(30.dp),
        contentDescription = null
        // ...
    )
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L480-L488
```

<br />

When you know the approximate size of your items after the data is asynchronously loaded, a good practice is to ensure your items' sizing remains the same before and after loading, for example, by adding some placeholders. This will help maintain the correct scroll position.

### Avoid nesting components scrollable in the same direction

This applies only to cases when nesting scrollable children without a predefined size inside another same direction scrollable parent. For example, trying to nest a child`LazyColumn`without a fixed height inside a vertically scrollable`Column`parent:

<br />

```kotlin
// throws IllegalStateException
Column(
    modifier = Modifier.verticalScroll(state)
) {
    LazyColumn {
        // ...
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L496-L503
```

<br />

Instead, the same result can be achieved by wrapping all of your composables inside one parent`LazyColumn`and using its DSL to pass in different types of content. This enables emitting single items, as well as multiple list items, all in one place:

<br />

```kotlin
LazyColumn {
    item {
        Header()
    }
    items(data) { item ->
        PhotoItem(item)
    }
    item {
        Footer()
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L512-L522
```

<br />

Keep in mind that cases where you're nesting different direction layouts, for example, a scrollable parent`Row`and a child`LazyColumn`, are allowed:

<br />

```kotlin
Row(
    modifier = Modifier.horizontalScroll(scrollState)
) {
    LazyColumn {
        // ...
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L531-L537
```

<br />

As well as cases where you still use the same direction layouts, but also set a fixed size to the nested children:

<br />

```kotlin
Column(
    modifier = Modifier.verticalScroll(scrollState)
) {
    LazyColumn(
        modifier = Modifier.height(200.dp)
    ) {
        // ...
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L546-L554
```

<br />

### Beware of putting multiple elements in one item

In this example, the second item lambda emits 2 items in one block:

<br />

```kotlin
LazyVerticalGrid(
    columns = GridCells.Adaptive(100.dp)
) {
    item { Item(0) }
    item {
        Item(1)
        Item(2)
    }
    item { Item(3) }
    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L570-L580
```

<br />

Lazy layouts will handle this as expected - they will lay out elements one after another as if they were different items. However, there are a couple of problems with doing so.

When multiple elements are emitted as part of one item, they are handled as one entity, meaning that they cannot be composed individually anymore. If one element becomes visible on the screen, then all elements corresponding to the item have to be composed and measured. This can hurt performance if used excessively. In the extreme case of putting all elements in one item, it completely defeats the purpose of using Lazy layouts. Apart from potential performance issues, putting more elements in one item will also interfere with`scrollToItem()`\&`animateScrollToItem()`.

However, there are valid use cases for putting multiple elements in one item, like having dividers inside a list. You do not want dividers to change scrolling indices, as they shouldn't be considered independent elements. Also, performance will not be affected as dividers are small. A divider will likely need to be visible when the item before it is visible, so they can be part of the previous item:

<br />

```kotlin
LazyVerticalGrid(
    columns = GridCells.Adaptive(100.dp)
) {
    item { Item(0) }
    item {
        Item(1)
        Divider()
    }
    item { Item(2) }
    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L589-L599
```

<br />

### Consider using custom arrangements

Usually Lazy lists have many items, and they occupy more than the size of the scrolling container. However, when your list is populated with few items, your design can have more specific requirements for how these should be positioned in the viewport.

To achieve this, you can use custom vertical[`Arrangement`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement)and pass it to the`LazyColumn`. In the following example, the`TopWithFooter`object only needs to implement the`arrange`method. Firstly, it will position items one after another. Secondly, if the total used height is lower than the viewport height, it will position the footer at the bottom:

<br />

```kotlin
object TopWithFooter : Arrangement.Vertical {
    override fun Density.arrange(
        totalSize: Int,
        sizes: IntArray,
        outPositions: IntArray
    ) {
        var y = 0
        sizes.forEachIndexed { index, size ->
            outPositions[index] = y
            y += size
        }
        if (y < totalSize) {
            val lastIndex = outPositions.lastIndex
            outPositions[lastIndex] = totalSize - sizes.last()
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L612-L628
```

<br />

### Consider adding`contentType`

Starting with Compose 1.2, in order to maximize the performance of your Lazy layout, consider adding[`contentType`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#extension-functions-summary)to your lists or grids. This allows you to specify the content type for each item of the layout, in cases where you're composing a list or a grid consisting of multiple different types of items:

<br />

```kotlin
LazyColumn {
    items(elements, contentType = { it.type }) {
        // ...
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt#L635-L639
```

<br />

When you provide the[`contentType`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#extension-functions-summary), Compose is able to reuse compositions only between the items of the same type. As reusing is more efficient when you compose items of similar structure, providing the content types ensures Compose doesn't try to compose an item of type A on top of a completely different item of type B. This helps maximize the benefits of composition reusing and your Lazy layout performance.

### Measuring performance

You can only reliably measure the performance of a Lazy layout when running in release mode and with R8 optimisation enabled. On debug builds, Lazy layout scrolling may appear slower. For more information on this, read through[Compose performance](https://developer.android.com/develop/ui/compose/performance).

## Additional resources

- [Create a finite scrollable list](https://developer.android.com/develop/ui/compose/quick-guides/content/finite-scrolling-list)
- [Create a scrollable grid](https://developer.android.com/develop/ui/compose/quick-guides/content/create-scrollable-grid)
- [Display nested scrolling items in a list](https://developer.android.com/develop/ui/compose/quick-guides/content/display-nested-list)
- [Filter a list while typing](https://developer.android.com/develop/ui/compose/quick-guides/content/filter-list-while-typing)
- [Lazily load data with lists and Paging](https://developer.android.com/develop/ui/compose/quick-guides/content/lazily-load-list)
- [Build a list using multiple item types](https://developer.android.com/develop/ui/compose/quick-guides/content/build-list-multiple-item-types)
- [Video: Lists in Compose](https://developer.android.com/develop/ui/compose/quick-guides/content/video/lists-in-compose)

## Recommended for you

- Note: link text is displayed when JavaScript is off
- [Migrate`RecyclerView`to Lazy list](https://developer.android.com/develop/ui/compose/migrate/migration-scenarios/recycler-view)
- [Save UI state in Compose](https://developer.android.com/develop/ui/compose/state-saving)
- [Kotlin for Jetpack Compose](https://developer.android.com/develop/ui/compose/kotlin)