Mastering LazyColumn: Keys & rememberSaveable for Stable Lists in Compose
Learn how to pair key-based item identity with rememberSaveable in Jetpack Compose’s LazyColumn to keep scroll position, preserve checkbox state across rotations, and cut unnecessary recompositions. Includes a working example, limits, and common pitfalls.
12 Feb 2026, 16:16 UTC

Problem: Unstable List Items in Compose
When you display a long list with LazyColumn, a common pain point is that items flicker, lose their UI state (e.g., a checkbox remains unchecked after rotation), or the scroll position jumps. These symptoms arise when Compose cannot reliably map a visual element to the underlying data, causing unnecessary recompositions and state loss.
Key‑Based Identity + rememberSaveable: The Practical Solution
Two Compose features together solve this problem:
- Key in
LazyColumn– tells Compose the stable identity of each list item. - rememberSaveable – hoists per‑item state to the SavedStateRegistry so it survives configuration changes.
With a stable key, Compose can diff the list efficiently, reusing existing composables when the underlying data set changes. Coupled with rememberSaveable, the UI state of each item (e.g., whether a checkbox is checked) is preserved across device rotations without needing a ViewModel.
Worked Example
Below is a minimal, self‑contained example that demonstrates the pattern. Assume you have a data class Item with a unique id and a mutable label.
@Composable
fun ItemList(items: List<Item>) {
// LazyColumn with a key based on the item’s unique id
LazyColumn {
items(items = items, key = { it.id }) { item ->
// rememberSaveable keeps the checked state across rotations
val checkedState = rememberSaveable { mutableStateOf(false) }
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = item.label, modifier = Modifier.weight(1f))
Checkbox(
checked = checkedState.value,
onCheckedChange = { checkedState.value = it }
)
}
}
}
}
Key points to note:
- The
keylambda usesit.id, which must be unique and stable across recompositions. rememberSaveablewraps aMutableStatethat holds the checkbox state. Because the state is a primitiveBoolean, Compose can automatically serialize it.- When the device rotates, the
LazyColumnpreserves the scroll position, and each checkbox remains in the same state as before.
Verification Steps
- Run the app and scroll to item #50.
- Check the checkbox next to item #50.
- Rotate the device.
- Verify that the scroll position is still at item #50 and the checkbox remains checked.
- Open Android Studio’s Layout Inspector to confirm that only the items whose state changed were recomposed.
Limits & Common Mistakes
1. Using Mutable Indices as Keys
A frequent mistake is key = { index }. If you reorder or filter the list, indices change, so Compose may think an item has moved and will recreate it, wiping any UI state. Always base the key on a stable property of the data object.
2. rememberSaveable vs remember
Use rememberSaveable only for state that must survive configuration changes. For transient UI state (e.g., focus, temporary input), remember is sufficient and cheaper.
3. Unsupported Types
By default, rememberSaveable can serialize Parcelable, Serializable, and primitive types. For custom objects, you must provide a Saver. Large or complex Savers increase serialization cost and can slow down recreation after rotation.
4. Heavy Work in Key Lambda
The key lambda runs on every recomposition. Avoid expensive calculations inside it; compute the key once outside the composable if possible.
5. Paging and Key Strategy
When using LazyPagingItems from Paging 3, ensure that the key strategy matches the paging source’s unique identifier to avoid duplicate items or state mismatches.
When rememberSaveable Won’t Help
- Process death:
rememberSaveableonly survives configuration changes. If the app is killed, state is lost unless you persist it elsewhere. - Non‑Parcelable/Serializable types without a custom
Savercannot be restored automatically.
Takeaway
By pairing a stable key with rememberSaveable in LazyColumn, you achieve:
- Consistent scroll position across rotations.
- Per‑item UI state that survives configuration changes.
- Reduced recompositions and memory churn for large lists.
Remember: the key must be unique and stable, and rememberSaveable should be reserved for state that truly needs persistence. With these practices, your Compose lists will feel fast, responsive, and reliable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.