remember is a function in Jetpack Compose that allows you to preserve a value between recompositions, preventing state reset on each restart of a Composable function. Without remember, all variables declared inside a function would reset on every UI update, making state unstable and useless. According to Google Android Developers, 2026, proper use of remember is the foundation of correct state management in declarative UI.
Key Takeaways
remember is a built-in Jetpack Compose function from the androidx.compose.runtime package that creates an object preserving its value between successive calls of a Composable function. Technically, remember works with slots — special memory cells allocated for each function call position in the UI tree. As long as the function remains in composition (not removed via an if-condition), remember will return the saved value on each recomposition.
The syntax of remember is simple: inside curly braces, you specify the block for computing the initial value. The block executes only once — during the first composition of the function. All subsequent recompositions return the already saved value without re-executing the block. However, if a key parameter is specified, the block re-executes when the key changes, allowing the value to be recalculated based on input data.
remember is not Kotlin magic — it is a simple function with a key parameter and a computation lambda. Its internal implementation uses Composer to read and write to slots. The Kotlin compiler, seeing a remember call, generates code that accesses the current CompositionContext and manages the slots. This means remember can only be called inside a Composable function or another function within the composition context.
In Jetpack Compose, there are three main variants of remember, each with its own purpose. The basic remember only preserves the value in the current process memory — on screen rotation (config change), Compose recreates the Composition, and all remember values are reset. To preserve data during configuration changes and process stopping, rememberSaveable is used.
rememberSaveable serializes the value into a Bundle via the SavedStateHandle or Parcelable mechanism. This allows surviving screen rotation, the "Don't keep activities" setting, and even temporary app minimization. However, rememberSaveable imposes restrictions on the type of data it can store: they must be primitives, Parcelable, Serializable, or support a Saver converter.
derivedStateOf is not a preservation mechanism but an optimization. It creates a State whose value is computed from other State objects. derivedStateOf reacts to changes in source states but recalculates the value only when there are subscribers. If the current recomposition does not read the derivedStateOf, the computation is not performed, saving resources on frequent but unneeded updates.
@Composable
fun RememberVariants() {
// 1. Basic remember: persists until leaving composition
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable: survives screen rotation
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf: computed only when needed
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("Created: $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("Button will be shown")
}
}
The choice between remember and rememberSaveable depends on how critical it is to preserve data during configuration changes. For temporary states that are not important to lose on rotation (animation, current scroll position, focus state) — regular remember is sufficient. For critical data (form text, selected items, checkboxes) — use rememberSaveable.
Performance of rememberSaveable is lower than that of regular remember because it requires serialization into a Bundle. Only use rememberSaveable for data that truly needs to survive Activity recreation. For everything else — plain remember. Excessive use of rememberSaveable leads to slowdowns on screen rotations and when switching between apps.
If you work with classes that do not support Parcelable or Serializable, use Saver — a converter that defines how to save and restore the object. Saver is described by a pair of lambdas: save (converts the object to a storable type) and restore (restores the object from saved data). A standard Saver is already implemented for mutableStateListOf and mutableStateMapOf.
| Characteristic | remember | rememberSaveable |
|---|---|---|
| Preservation during recomposition | Yes | Yes |
| Preservation on rotation | No | Yes |
| Preservation on process stop | No | Yes |
| Type requirements | Any | Parcelable, Serializable, Saver |
| Performance | High | Medium |
Consider a typical scenario — a profile editing screen where remember is used for several purposes: storing form field state, calculating derived values, and caching computationally expensive operations.
data class ProfileUiState(
val name: String = "",
val bio: String = "",
val isSaving: Boolean = false
)
@Composable
fun ProfileEditor() {
var state by rememberSaveable { mutableStateOf(ProfileUiState()) }
val isValid = remember {
derivedStateOf { state.name.isNotBlank() && state.bio.length() <= 500 }
}
val bioWarning = remember(state.bio) {
if (state.bio.length() > 400) {
"${500 - state.bio.length} chars left"
} else null
}
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = state.name,
onValueChange = { state = state.copy(name = it) },
label = { Text("Name") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("About") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("Save")
}
}
}
In the example, state is saved via rememberSaveable — text will not be lost on screen rotation. isValid is computed via derivedStateOf, which prevents unnecessary computations during recompositions. bioWarning uses remember with the bio key — this is an expensive computation (optional, for demonstration) that recalculates only when bio changes, not on every recomposition.
Derived states are values that are computed from other State objects. Instead of computing them on every recomposition and wasting CPU on identical results, remember with derivedStateOf computes the value only when sources change. This is especially useful for filtering, sorting, and data aggregation.
remember with keys (remember(key) { calculation }) is another optimization mechanism. If the key has not changed since the last recomposition, the computation block is not executed, and the cached value is returned. This is convenient for caching objects whose creation is expensive: date formatting, JSON parsing, creating large immutable collections.
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
// derivedStateOf: filter recomputed only when inputs change
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember with key: formatted stats recomputed only on query change
val statsText = remember(query) {
"Results for query \"$query\": ${filtered.value.size}"
}
Text(statsText)
LazyColumn {
items(filtered.value, key = { it.id }) { item ->
Text(item.title)
}
}
}
filtered is a derivedStateOf that automatically recalculates when allItems or query changes. statsText uses remember(query) — expensive string formatting is performed only when the search query changes. The combination of derivedStateOf and remember with keys gives maximum performance: derived state is computed only when needed, and complex objects are cached until the key changes.
Frequently Asked Questions
No, remember is a function from the compose.runtime package that requires a CompositionContext. It can only be called inside a @Composable function or inside another function called from a Composable. To store data outside composition, use ViewModel.
Without a key, remember executes the computation block only once — during the first composition. All subsequent recompositions return the saved value. If you need to recalculate the value when data changes, be sure to specify them as a key: remember(data) { compute(data) }.
There is no direct way to reset remember. The only way is to remove the function from composition (for example, hiding it with an if-condition) and then show it again. On re-entry, the remember block executes again, creating a new initial value.
remember stores state in a Composable function slot and lives as long as the function is in composition. ViewModel lives as long as the screen lifecycle. ViewModel is preserved on rotation and is used for business logic. remember is for local UI state that is not needed outside a single function.
Yes, remember works correctly in @Preview Composable functions because Preview creates a proper CompositionContext. However, rememberSaveable may not work correctly in Preview because SavedStateHandle may be absent in the preview environment.
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