Choosing State Persistence in Jetpack Compose: remember vs. rememberSaveable vs. ViewModel
Learn how to choose between remember, rememberSaveable, and ViewModel in Jetpack Compose to ensure your UI state survives rotations and process death without bloating your architecture.
22 Mar 2026, 14:48 UTC

The State Persistence Dilemma
In Jetpack Compose, the primary challenge is not creating state, but deciding where it lives so it survives the right events. If you store a user's input in a simple remember block, that data vanishes the moment the user rotates their screen. If you put every single toggle switch into a ViewModel, you create an architectural bottleneck and unnecessary boilerplate.
The goal is to match the state's lifecycle to the specific event it must survive: a recomposition, a configuration change, or a system-initiated process death.
Persistence Options Comparison
| Mechanism | Survives Recomposition | Survives Config Change | Survives Process Death | Storage Limit |
|---|---|---|---|---|
remember |
Yes | No | No | Heap Memory |
rememberSaveable |
Yes | Yes | Yes | Bundle (Small) |
ViewModel |
Yes | Yes | No (unless using SavedStateHandle) | Heap Memory |
Decision Trade-offs
When to use remember
Use remember for transient UI state that has no value outside the current visual session. Examples include animation progress, a temporary hover state, or the current index of a carousel. Since it lives in the Composition, it is the most performant option with the least overhead.
When to use rememberSaveable
Use rememberSaveable for simple UI state that must survive a screen rotation or when the OS kills the app to reclaim memory (process death). This is ideal for text field inputs or a checkbox state. Because it uses the Android Bundle mechanism, you are limited to types that can be put into a Bundle (Primitives, Parcelables). Attempting to store large bitmaps or complex lists here can trigger a TransactionTooLargeException.
When to use ViewModel
Use a ViewModel for business-critical state, data fetched from a repository, or state shared across multiple screens. ViewModels survive configuration changes because they are scoped to the Activity or Navigation Graph rather than the Composition. This separates the "what" (business logic) from the "how" (UI rendering).
Implementation: Hybrid State Strategy
The most efficient architecture uses a hybrid approach: ViewModel for the source of truth and remember for local UI polish. This prevents "prop drilling," where state is passed through five layers of components just to reach a button.
// Required dependencies: androidx.lifecycle:lifecycle-viewmodel-compose
class UserProfileViewModel : ViewModel() {
// Business state: Survives rotation, cleared when user leaves screen
var username by mutableStateOf("Guest")
private set
fun updateUsername(newName: String) {
username = newName
}
}
@Composable
fun UserProfileScreen(viewModel: UserProfileViewModel = viewModel()) {
// UI state: Survives rotation and process death (e.g., draft text)
var draftName by rememberSaveable { mutableStateOf("") }
// Transient state: Lost on rotation (e.g., is the edit field focused)
var isEditing by remember { mutableStateOf(false) }
Column {
Text("Current User: ${viewModel.username}")
if (isEditing) {
TextField(
value = draftName,
onValueChange = { draftName = it }
)
Button(onClick = {
viewModel.updateUsername(draftName)
isEditing = false
}) {
Text("Save")
}
} else {
Button(onClick = { isEditing = true }) {
Text("Edit Name")
}
}
}
}
Verification and Diagnostics
To verify your state implementation, perform these three checks in an emulator:
- Rotation Test: Enter text in a field and rotate the device. If the text disappears, you used
rememberwhererememberSaveableor aViewModelwas required. - Process Death Test: Open the app, put it in the background, and run
adb shell am kill <package_name>from your terminal. Re-open the app. OnlyrememberSaveableandSavedStateHandle(in ViewModels) will persist. - Memory Check: If you notice lag during screen transitions, check if you are storing large objects in
rememberSaveable. Move those objects to aViewModelor a local database.
Rollback and Refactoring
If you find that a ViewModel has become bloated with UI-only state (like isExpanded flags), refactor by hoisting that state back down to the Composable using rememberSaveable. This reduces the ViewModel's memory footprint and makes the Composable more reusable in different contexts.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.