Persisting UI State in Jetpack Compose with rememberSaveable: How, When, and What to Avoid
Learn how Jetpack Compose’s rememberSaveable keeps UI state across rotations and process death, the right way to use it, and the limits that can trip you up.
30 Jul 2025, 09:47 UTC

What rememberSaveable Does
Jetpack Compose’s rememberSaveable is a lightweight state holder that survives configuration changes such as screen rotations. Under the hood it writes the value into a Bundle that the framework restores when the activity is recreated. When the app is killed and later restarted, the same mechanism can restore the state if the value is parcelable and the SaveableStateRegistry is active.
How It Works Under the Hood
When a composable calls rememberSaveable, Compose registers a Saver with the SaveableStateRegistry. The registry serializes the value to a Bundle using either the default Parcelable implementation or a custom Saver you provide. On recreation, Compose retrieves the serialized value from the bundle and re‑initialises the state. The entire process is transparent to the developer; you only need to ensure the type you store can be written to and read from a bundle.
Practical Code Example
Below is a minimal counter that survives rotations and process death.
@Composable
fun CounterScreen() {
// The counter is stored in a Bundle automatically
var count by rememberSaveable { mutableStateOf(0) }
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Count: $count", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
To store a custom data class, annotate it with @Parcelize or supply a Saver:
@Parcelize
data class User(val id: Int, val name: String) : Parcelable
@Composable
fun UserProfile(user: User) {
var editedName by rememberSaveable(user) { mutableStateOf(user.name) }
// editing logic here
}
When to Use It
- Lightweight UI state that is tightly coupled to a composable (e.g., form input, toggle state).
- State that can be fully represented by primitives,
String,Parcelable, orSerializable. - When you want automatic restoration without wiring a
ViewModel.
Limits and Common Pitfalls
| Limitation | Impact |
|---|---|
| Bundle size (~1 MB) | Large objects or many entries can cause TransactionTooLargeException during rotation. |
| Mutable objects | Changes after restoration may be lost; ensure immutability or re‑apply changes. |
Missing @Parcelize or Saver | Runtime IllegalStateException when Compose tries to serialize. |
| Over‑use | Excessive state in the bundle slows down recreation and increases memory pressure. |
| Scope to composition | State disappears if the composable is removed from the tree; not a replacement for ViewModel for business logic. |
Typical mistakes:
- Storing a large data structure (e.g., a list of thousands of items) directly in
rememberSaveable. - Using a mutable collection without wrapping it in an immutable wrapper.
- Assuming
rememberSaveablesurvives process death without ensuring the type is parcelable. - Forgetting to provide a
Saverfor a custom class that isn’t parcelable.
Testing the Persistence
- Rotation test: Run the app, rotate the device, and verify the counter value remains unchanged.
- Process death test: Enable "Don't keep activities" in developer options, navigate away, and return. The state should still be preserved.
- Bundle size check: Use
adb shell dumpsys activity --sizeto inspect the saved state bundle and confirm it stays well below 1 MB. - Layout Inspector: Open Android Studio’s Layout Inspector after a rotation and look for the
rememberSaveableentry under the composable’s hierarchy.
These checks give confidence that rememberSaveable behaves as expected in your app’s lifecycle.
Takeaway
Use rememberSaveable for small, UI‑only state that must survive rotations and, optionally, process death. Keep the stored data parcelable, avoid bloating the bundle, and reserve ViewModel for heavier or cross‑screen state. When in doubt, test the rotation and process‑death scenarios to catch serialization errors early.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.