Depending on where your state is hoisted to and the logic that is required, you can use different APIs to store and restore your[UI state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state). Every app uses a combination of APIs to best achieve this.
| **Note:** You can see more information on where to hoist your state based on the logic applied in the[Where to hoist state](https://developer.android.com/develop/ui/compose/state-hoisting)documentation.

Any Android app could lose its[UI state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state)due to activity or process recreation. This loss of state can occur because of the following events:

- [Configuration changes](https://developer.android.com/guide/topics/resources/runtime-changes). The activity is destroyed and recreated unless the configuration change is[handled manually](https://developer.android.com/guide/topics/resources/runtime-changes#HandlingTheChange).
- [System-initiated process death](https://developer.android.com/topic/libraries/architecture/saving-states#ui-dismissal-system). The app is in the background and the device frees up resources (like memory) to be used by other processes.

| **Note:** [System-initiated process death](https://developer.android.com/topic/libraries/architecture/saving-states#ui-dismissal-system)is different from[user-initiated process death](https://developer.android.com/topic/libraries/architecture/saving-states#ui-dismissal-user), in which the user explicitly dismisses the activity. In the case of user-initiated process death, the loss of transient state is generally reasonable (for example, losing the animation state or the content of a[`TextField`](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#TextField(kotlin.String,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Boolean,androidx.compose.ui.text.TextStyle,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Boolean,androidx.compose.ui.text.input.VisualTransformation,androidx.compose.foundation.text.KeyboardOptions,androidx.compose.foundation.text.KeyboardActions,kotlin.Boolean,kotlin.Int,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.compose.material.TextFieldColors))while filling out a form).

Preserving the state after these events is essential for a positive user experience. Selecting which state to persist depends on your app's unique user flows. As a best practice, you should at least preserve user input and navigation-related state. Examples of this include the scroll position of a list, the ID of the item the user wants more detail about, the in-progress selection of user preferences, or input in text fields.

This page summarizes the APIs available to store UI state depending on where your state is hoisted to and the logic that needs it.

## UI logic

If your state is hoisted in the UI, either in composable functions or plain state holder classes scoped to the Composition, you can use[`rememberSaveable`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/package-summary#rememberSaveable(kotlin.Array,androidx.compose.runtime.saveable.Saver,kotlin.String,kotlin.Function0))to retain state across activity and process recreation.

In the following snippet,`rememberSaveable`is used to store a single boolean UI element state:

<br />

```kotlin
@Composable
fun ChatBubble(
    message: Message
) {
    var showDetails by rememberSaveable { mutableStateOf(false) }

    ClickableText(
        text = AnnotatedString(message.content),
        onClick = { showDetails = !showDetails }
    )

    if (showDetails) {
        Text(message.timestamp)
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/state/SavingUIStateSnippets.kt#L44-L58
```

<br />

<br />

**Figure 1**. Chat message bubble expands and collapses when tapped.

<br />

`showDetails`is a boolean variable that stores if the chat bubble is collapsed or expanded.
| **Important:** Usually, data stored in saved instance state is transient state that depends on user input or navigation. Examples of this include the scroll position of a list, the ID of the item the user wants more detail about, the in-progress selection of user preferences, or input in text fields.

`rememberSaveable`stores[UI element state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state)in a[`Bundle`](https://developer.android.com/reference/android/os/Bundle)through the saved instance state mechanism.

It is able to store primitive types to the bundle automatically. If your state is held in a type that is not primitive, like a data class, you can use different storing mechanisms, such as using the[`Parcelize`](https://developer.android.com/kotlin/parcelize)annotation, using Compose APIs like[`listSaver`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/package-summary#listSaver(kotlin.Function2,kotlin.Function1))and[`mapSaver`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/package-summary#mapSaver(kotlin.Function2,kotlin.Function1)), or implementing a custom saver class extending Compose runtime[`Saver`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/Saver)class. See the[Ways to store state](https://developer.android.com/develop/ui/compose/state#ways-to-store)documentation to learn more about these methods.

In the following snippet, the[`rememberLazyListState`](https://cs.android.com/androidx/platform/tools/dokka-devsite-plugin/+/master:testData/compose/source/androidx/compose/foundation/lazy/LazyListState.kt;l=49?q=LazyListState)Compose API stores[`LazyListState`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListState), which consists of the scroll state of a[`LazyColumn`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyColumn(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1))or[`LazyRow`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyRow(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.ui.Alignment.Vertical,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)), using`rememberSaveable`. It uses a[`LazyListState.Saver`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt;l=413?q=LazyListState.kt), which is a custom saver that is able to store and restore the scroll state. After an activity or process recreation (for example, after a configuration change like changing device orientation), the scroll state is preserved.

<br />

```kotlin
@Composable
fun rememberLazyListState(
    initialFirstVisibleItemIndex: Int = 0,
    initialFirstVisibleItemScrollOffset: Int = 0
): LazyListState {
    return rememberSaveable(saver = LazyListState.Saver) {
        LazyListState(
            initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset
        )
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/state/SavingUIStateSnippets.kt#L64-L74
```

<br />

### Best practice

`rememberSaveable`uses a[`Bundle`](https://developer.android.com/reference/android/os/Bundle)to store UI state, which is shared by other APIs that also write to it, like[`onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState(android.os.Bundle))calls in your activity. However, the size of this`Bundle`is limited, and storing large objects could lead to[`TransactionTooLarge`](https://developer.android.com/reference/android/os/TransactionTooLargeException)exceptions in runtime. This can be particularly problematic in single`Activity`apps where the same`Bundle`is being used across the app.

To avoid this type of crash,*you should not store large complex objects or lists of objects in the bundle*.

Instead, store the minimum state required, like IDs or keys, and use these to delegate restoring more complex UI state to other mechanisms, like[persistent storage](https://developer.android.com/topic/libraries/architecture/saving-states#local).
| **Note:** In some cases, it might be acceptable not to store all UI elements' state.

These design choices depend on the specific use cases for your app and how your users expect it to behave.

### Verify state restoration

You can verify that the state stored with[`rememberSaveable`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/package-summary#rememberSaveable(kotlin.Array,androidx.compose.runtime.saveable.Saver,kotlin.String,kotlin.Function0))in your Compose elements is correctly restored when the activity or process is recreated. There are specific APIs to achieve this, such as[`StateRestorationTester`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester). Check out the[Testing](https://developer.android.com/develop/ui/compose/testing#verify_state_restoration)documentation to learn more.

## Business logic

If your[UI element state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state)is hoisted to the`ViewModel`because it is required by business logic, you can use`ViewModel`'s APIs.

One of the main benefits of using a`ViewModel`in your Android application is that it handles configuration changes for free. When there is a configuration change, and the activity is destroyed and recreated, the UI state hoisted to the`ViewModel`is kept in memory. After the recreation, the old`ViewModel`instance is attached to the new activity instance.
| **Note:** The`ViewModel`, as the implementation of a screen level state holder, handles the business logic used to produce[screen UI state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state). You should hoist the UI state to the`ViewModel`, not because it will handle configuration changes for free, but because it makes sense for your architecture.

However, a`ViewModel`instance does not survive system-initiated process death. To have the UI state survive this, use the[Saved State module for ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate#savedstate-compose-state), which contains the[`SavedStateHandle`](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle)API.

### Best practice

[`SavedStateHandle`](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle)also uses the`Bundle`mechanism to store UI state, so you should only use it to store simple[UI element state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state).

[Screen UI state](https://developer.android.com/topic/architecture/ui-layer/stateholders#ui-state), which is produced by applying business rules and accessing layers of your application other than UI, should not be stored in`SavedStateHandle`due to its potential complexity and size. You can use different mechanisms to store complex or large data, like[local persistent storage](https://developer.android.com/topic/libraries/architecture/saving-states#local). After a process recreation, the screen is recreated with the restored transient state that was stored in`SavedStateHandle`(if any), and the screen UI state is produced again from the data layer.
| **Note:** For more information on the different ways of saving UI state, see the[Save UI states](https://developer.android.com/topic/libraries/architecture/saving-states)documentation.

### `SavedStateHandle`APIs

[`SavedStateHandle`](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle)has different APIs to store UI element state, most notably:

|              Compose[`State`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/State)              | [`saveable()`](https://developer.android.com/reference/kotlin/androidx/lifecycle/viewmodel/compose/package-summary#(androidx.lifecycle.SavedStateHandle).saveable(kotlin.String,androidx.compose.runtime.saveable.Saver,kotlin.Function0)) |
| [`StateFlow`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/) |                                                   [`getStateFlow()`](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle#getStateFlow(kotlin.String,kotlin.Any))                                                   |
|---------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### Compose`State`

Use the`saveable`API of`SavedStateHandle`to read and write UI element state as`MutableState`, so it survives activity and process recreation with minimal code setup.

The`saveable`API supports primitive types out of the box and receives a`stateSaver`parameter to use custom savers, just like`rememberSaveable()`.

In the following snippet,`message`stores the user input types into a`TextField`:

<br />

```kotlin
class ConversationViewModel(
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) {
        mutableStateOf(TextFieldValue(""))
    }
        private set

    fun update(newMessage: TextFieldValue) {
        message = newMessage
    }

    /*...*/
}

val viewModel = ConversationViewModel(SavedStateHandle())

@Composable
fun UserInput(/*...*/) {
    TextField(
        value = viewModel.message,
        onValueChange = { viewModel.update(it) }
    )
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/state/SavingUIStateSnippets.kt#L81-L105
```

<br />

See the[`SavedStateHandle`](https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate#savedstate-compose-state)documentation for more information on using the`saveable`API.
| **Caution:** The[saveable](https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate#savedstate-compose-state)API is experimental.

#### `StateFlow`

Use[`getStateFlow()`](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle#getStateFlow(kotlin.String,kotlin.Any))to store UI element state and consume it as a flow from the[`SavedStateHandle`](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle). The[`StateFlow`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/)is read only, and the API requires you to specify a key so you can replace the flow to emit a new value. With the key you configured, you can retrieve the`StateFlow`and collect the latest value.

In the following snippet,`savedFilterType`is a`StateFlow`variable that stores a filter type applied to a list of chat channels in a chat app:

<br />

```kotlin
private const val CHANNEL_FILTER_SAVED_STATE_KEY = "ChannelFilterKey"

class ChannelViewModel(
    channelsRepository: ChannelsRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val savedFilterType: StateFlow<ChannelsFilterType> = savedStateHandle.getStateFlow(
        key = CHANNEL_FILTER_SAVED_STATE_KEY, initialValue = ChannelsFilterType.ALL_CHANNELS
    )

    private val filteredChannels: Flow<List<Channel>> =
        combine(channelsRepository.getAll(), savedFilterType) { channels, type ->
            filter(channels, type)
        }.onStart { emit(emptyList()) }

    fun setFiltering(requestType: ChannelsFilterType) {
        savedStateHandle[CHANNEL_FILTER_SAVED_STATE_KEY] = requestType
    }

    /*...*/
}

enum class ChannelsFilterType {
    ALL_CHANNELS, RECENT_CHANNELS, ARCHIVED_CHANNELS
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/state/SavingUIStateSnippets.kt#L117-L142
```

<br />

Every time the user selects a new filter type,`setFiltering`is called. This saves a new value in`SavedStateHandle`stored with the key`_CHANNEL_FILTER_SAVED_STATE_KEY_`.`savedFilterType`is a flow emitting the latest value stored to the key.`filteredChannels`is subscribed to the flow to perform the channel filtering.

See the[`SavedStateHandle`](https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate#savedstate-stateflow)documentation for more information on the`getStateFlow()`API.

### Summary

The following table summarizes the APIs covered in this section, and when to use each to save UI state:

|             Event              |      UI logic      | Business logic in a`ViewModel` |
|--------------------------------|--------------------|--------------------------------|
| Configuration changes          | `rememberSaveable` | Automatic                      |
| System-initiated process death | `rememberSaveable` | `SavedStateHandle`             |

The API to use depends on where the state is held and the logic that it requires. For state that is used in[UI logic](https://developer.android.com/topic/architecture/ui-layer/stateholders#logic), use`rememberSaveable`. For state that is used in[business logic](https://developer.android.com/topic/architecture/ui-layer/stateholders#logic), if you hold it in a`ViewModel`, save it using`SavedStateHandle`.

You should use the bundle APIs (`rememberSaveable`and`SavedStateHandle`) to store small amounts of UI state. This data is the minimum necessary to restore the UI back to its previous state, together with other storing mechanisms. For example, if you store the ID of a profile the user was looking at in the bundle, you can fetch heavy data, like profile details, from the data layer.

For more information on the different ways of saving UI state, see the general[Saving UI State documentation](https://developer.android.com/topic/libraries/architecture/saving-states)and the[data layer](https://developer.android.com/topic/architecture/data-layer)page of the architecture guide.

## Recommended for you

- Note: link text is displayed when JavaScript is off
- [Where to hoist state](https://developer.android.com/develop/ui/compose/state-hoisting)
- [State and Jetpack Compose](https://developer.android.com/develop/ui/compose/state)
- [Lists and grids](https://developer.android.com/develop/ui/compose/lists)