Choosing between remember and rememberSaveable in Jetpack Compose for state persistence
Learn when to use Jetpack Compose's remember versus rememberSaveable to keep UI state across rotations, with a concrete counter example, trade‑offs, and verification steps.
04 Oct 2026, 17:05 UTC

Problem: UI state disappears after rotation
When you build a screen with Jetpack Compose, any state held in a remember block is lost as soon as the configuration changes (for example, when the device rotates). Users notice this when a counter resets, a form clears, or a selected tab jumps back to the default. The fix is to persist that state across the recreation of the activity or fragment.
Understanding the two state holders
Compose offers two primitives for holding values in the composition:
remember– keeps an object for the lifetime of the composition. It does not survive process recreation or configuration changes.rememberSaveable– behaves likerememberbut also writes the held value into aBundlethat Android saves and restores during configuration changes and process death.
The key difference is that rememberSaveable requires the stored value to be parcelable, or you must supply a custom Saver. If the value does not meet this requirement, Compose throws an IllegalStateException at the moment the state needs to be saved.
Worked example: a rotation‑proof counter
The following composable uses rememberSaveable to keep a counter value across rotations:
@Composable
fun Counter() {
var count by rememberSaveable { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: $count", style = MaterialTheme.typography.h5)
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
}
Place this composable inside the setContent block of your activity. When you tap the button a few times and then rotate the device (Ctrl+F11 in the emulator or the physical rotation gesture), the displayed number stays the same.
Trade‑off and limitation
Using rememberSaveable adds a small runtime cost because Compose must serialize the value into a Bundle. For simple types like Int, String, or Parcelable data classes this cost is negligible. For complex, non‑parcelable objects you have two options:
- Make the class implement
Parcelable(or use@Parcelizewith Kotlin). - Provide a custom
Saverthat knows how to convert the object to a Bundle‑friendly representation and back.
If you forget to do either, the app will crash on the next configuration change. The error message will mention that the type is not saved because it lacks a saver.
How to verify the behavior yourself
- Create a new Android Studio project with the Compose BOM set to a recent version (e.g.,
androidx.compose:compose-bom:2025.08.00). - Add the
Countercomposable above to the default activity’ssetContent. - Run the app on an emulator or a physical device.
- Tap the Increment button several times to raise the count.
- Rotate the device (Ctrl+F11 in the emulator). Observe that the count remains unchanged.
- Replace
rememberSaveablewith plainrememberand repeat the rotation. The count will reset to zero, demonstrating the loss of state. - To test the saver requirement, try storing a non‑parcelable data class without a custom saver inside
rememberSaveable. Rotate the device; the app will throw anIllegalStateExceptionconfirming the parcelable constraint.
These steps let you confirm both the persistence guarantee and the limitation without needing any special instrumentation.
Closing recommendation
Choose rememberSaveable for any UI state that must survive a configuration change—form fields, selected tabs, counters, or scroll positions. Use plain remember for temporary values that are tied to the current composition, such as animation progress, hover flags, or intermediate calculation results. This approach avoids unnecessary serialization overhead while giving you confidence that critical user data will not be lost when the device rotates or the system recreates your activity.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.