If you’ve built Android apps with Jetpack Compose, you’ve probably run into the question: Should I use ViewModel or rememberSaveable? Both help you keep state alive, but they work very differently depending on what’s happening to your app — like when the screen rotates or when the system kills your process.
This post will break down ViewModel and rememberSaveable, explain when to use each, and show real code examples so it finally clicks.
The Basics: Why State Preservation Matters
On Android, your app doesn’t always stay alive. Two big events affect your app’s state:
- Configuration changes — like screen rotations, language changes, or switching dark mode. The activity is destroyed and recreated, but the process usually stays alive.
- Process death — when Android kills your app’s process (e.g., to reclaim memory) and later restores it when the user comes back.
If you don’t handle these correctly, your users lose whatever they were doing. That’s where ViewModel and rememberSaveable come in.
remember: The Starting Point in Compose
At the simplest level, you use remember
in Jetpack Compose to keep state alive across recompositions.
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
- Here,
count
won’t reset when Compose redraws the UI. - But if the device rotates (configuration change), the state is lost because
remember
only survives recompositions, not activity recreation.
That’s why we need more powerful tools.
rememberSaveable: Survives Configuration Changes and Process Death
rememberSaveable
goes one step further. It automatically saves your state into a Bundle
using Android’s saved instance state mechanism.
@Composable
fun CounterScreen() {
var count by rememberSaveable { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
What happens here:
- Rotate the screen?
count
survives. - App is killed and restored (process death)?
count
also survives, because it was written to the saved instance state.
Limitations:
- Only works with data types that can be written to a
Bundle
(primitives, Strings, parcelables, etc.). - Not ideal for large objects or data fetched from a repository.
ViewModel: Survives Configuration Changes, Not Process Death
A ViewModel
is a lifecycle-aware container designed to hold UI data. It’s tied to a LifecycleOwner
like an activity or a navigation back stack entry.
class CounterViewModel : ViewModel() {
var count by mutableStateOf(0)
}
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
Button(onClick = { viewModel.count++ }) {
Text("Count: ${viewModel.count}")
}
}
What happens here:
- Rotate the screen?
count
survives. The sameViewModel
instance is reused. - App is killed (process death)?
count
is lost. TheViewModel
does not persist beyond process death.
Configuration Changes vs Process Death: Who Wins?
Here’s the clear breakdown:

When to Use rememberSaveable
Use rememberSaveable for small, lightweight UI state that:
- Must survive both rotation and process death.
- Can easily be serialized into a
Bundle
.
Examples:
- Current tab index.
- Form text fields.
- Simple filter/sort options.
When to Use ViewModel
Use ViewModel for more complex or long-lived state that:
- Doesn’t need to survive process death.
- Might involve business logic, repositories, or data streams.
- Should be scoped to the screen or navigation graph.
Examples:
- Data loaded from a database or network.
- Complex business logic.
- State shared across multiple composables in the same screen.
Can You Combine Them? Yes.
Often, the best solution is to use ViewModel and rememberSaveable together.
For example, a ViewModel
manages your main UI state, but a few critical fields use rememberSaveable
so they’re restored even after process death.
@Composable
fun FormScreen(viewModel: FormViewModel = viewModel()) {
var userInput by rememberSaveable { mutableStateOf("") }
Column {
TextField(
value = userInput,
onValueChange = { userInput = it }
)
Button(onClick = { viewModel.submit(userInput) }) {
Text("Submit")
}
}
}
Here:
userInput
is lightweight and saved withrememberSaveable
.- The
ViewModel
takes care of processing and persisting the submitted data.
Conclusion
The truth about ViewModel and rememberSaveable is simple once you think in terms of configuration changes vs process death:
remember
→ Only survives recomposition.rememberSaveable
→ Survives both rotation and process death (small, serializable state).ViewModel
→ Survives rotation, great for business logic, but not process death.
Use them in combination, not competition. Each tool has its place, and knowing when to reach for which makes your Compose apps more resilient, smoother, and user-friendly.