Jetpack Compose remember: retain UI state across recompositions
Use Jetpack Compose remember to keep expensive UI objects alive across recompositions without rebuilding them on every pass.
30 Mar 2026, 16:13 UTC

Problem: recomposition rebuilds objects inside composable functions
Jetpack Compose re-executes a composable function whenever the state it reads changes. Any object created directly in the function body is recreated on every recomposition. remember stores the result of a lambda in a composition-scoped map keyed by the composable’s position in the UI tree, so the same instance is returned on later passes without re-running the creation code.
Compose identifies each composable by its call site and position in the UI tree, not by the variable name. If you move the composable or change its call-site order, the key changes and Compose treats it as a different instance.
Worked example: cache a decoded bitmap
This example keeps a decoded bitmap in memory while the screen remains in the composition. Decoding is expensive, so the bitmap should survive recompositions caused by unrelated state changes.
@Composable
fun UserAvatar(imageUrl: String) {
val decodedBitmap = remember(key = imageUrl) {
decodeBitmap(imageUrl)
}
Image(
bitmap = decodedBitmap,
contentDescription = "User avatar"
)
}
The key = imageUrl argument tells Compose to discard the previous bitmap and run the lambda again only when the URL changes. Without the key, Compose returns the first bitmap even after the URL parameter changes, which leaves stale state on screen.
Choosing between remember, rememberSaveable, and ViewModel
| Scope | Survives recomposition | Survives process death | Typical use |
|---|---|---|---|
remember | Yes | No | UI-only cached objects, temporary calculations |
rememberSaveable | Yes | Yes, if the value is parcelable or saved | Form input, scroll position, simple UI state |
| ViewModel | Yes | Yes, across configuration changes | Business logic, screen-level state, repository access |
Where to run verification
Open Android Studio on a development machine. Run the Compose preview or an instrumented test on a connected device or emulator. Add Log.d("Remember", "decoding " + imageUrl) inside the lambda. Trigger a recomposition by changing unrelated state in the same screen. The log should appear once per unique URL, not on every recomposition. Call remember only from the UI thread during composition; calling it from a background coroutine is unsupported.
Limits and common mistakes
- Process death clears the map.
remembersurvives recomposition but not process death. For state that must return after the app is killed and restarted, userememberSaveableor a ViewModel. - Large objects increase memory pressure. Storing full database cursors or large lists in
rememberkeeps them alive as long as the composable stays in the composition. Keep stored values small or move them to a ViewModel. - Non-UI objects leak. Do not store database connections, network clients, or other lifecycle-bound resources in
remember. They outlive the UI and can prevent garbage collection. - Missing keys cause stale state. If a remembered value depends on a parameter and you omit
key, Compose returns the old value after the parameter changes.
Practical check
Use Android Studio’s Recompose tool window. Select the composable, trigger a state update, and confirm the recomposition count does not rise for the remembered subtree when the remembered value is unchanged. If the count rises, the lambda is running again or the key is unstable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.