remember vs rememberSaveable in Jetpack Compose: When to Use Each

Jetpack Compose gives you two closely related functions for holding UI state: remember and rememberSaveable. They look almost identical in code but behave very differently when your app is rotated, backgrounded, or process-killed by the system. Picking the wrong one is a common source of “why did my state just disappear” bugs.

What remember Actually Does

remember stores a value in the Composition — the in-memory tree Compose builds to describe your UI. That value survives recomposition (Compose re-running your composable function because some input changed), but it does not survive a configuration change like a screen rotation, and it does not survive the process being killed and restored.

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}

Rotate the device while this is on screen, and count resets to 0. The Activity is recreated, the Composition is thrown away and rebuilt, and remember has nothing to restore from.

What rememberSaveable Adds

rememberSaveable does the same job as remember, but it also saves the value into the same Bundle-based mechanism Android uses for onSaveInstanceState. That means it survives configuration changes and, in most cases, process death followed by system-initiated restoration (for example, when Android kills a backgrounded app to reclaim memory and the user navigates back to it).

@Composable
fun Counter() {
    var count by rememberSaveable { mutableStateOf(0) }

    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}

The only code change is swapping the function name. Now the count survives rotation.

Why Not Just Use rememberSaveable Everywhere?

Two reasons:

  • Not every type can be saved automatically. rememberSaveable relies on the value being Parcelable, serializable, or one of a small set of natively supported types (numbers, strings, booleans, and a few others). A complex custom object won’t work without extra effort.
  • It’s unnecessary overhead for state that shouldn’t survive rotation anyway — for example, whether a dropdown menu is currently open. Losing that on rotation is fine and arguably correct behavior.

Saving a Custom Object

For a data class that isn’t automatically saveable, use a custom Saver, or simpler — make the class @Parcelize if it lives in an Android module:

@Parcelize
data class FormState(val name: String, val email: String) : Parcelable

@Composable
fun SignupForm() {
    var form by rememberSaveable { mutableStateOf(FormState("", "")) }
    // ...
}

Quick Decision Rule

Ask: “If the screen rotates right now, should this value still be there?” If the answer is yes — form input, a counter, a selected tab — use rememberSaveable. If the answer is no, or the value is derived from something else (like an animation’s current frame), plain remember is enough and cheaper.

For state that needs to survive beyond the UI layer entirely — across navigation, or shared between screens — neither of these is the right tool; that’s what a ViewModel with a StateFlow is for, which we cover in our guide to Jetpack Compose state management.