# StateKeeper vs InstanceKeeper in Decompose: When to Use Each for State Management

> Understand StateKeeper vs InstanceKeeper in Decompose. Learn when to use StateKeeper for persistent data and InstanceKeeper for non-serializable objects to optimize your state management.

- Repository: [Arkadii Ivanov/decompose](https://github.com/arkivanov/decompose)
- Tags: deep-dive
- Published: 2026-02-25

---

**StateKeeper persists serializable data across process death and configuration changes, while InstanceKeeper retains non-serializable object instances only across component recreation, not process death.**

When building multiplatform applications with Decompose (arkivanov/decompose), managing state across Android configuration changes and process recreation requires understanding two distinct mechanisms. While both **StateKeeper** and **InstanceKeeper** help survive component destruction, they serve fundamentally different purposes—one handles serialization for process survival, while the other retains object instances for configuration changes.

## Core Differences Between StateKeeper and InstanceKeeper

| Aspect | StateKeeper | InstanceKeeper |
|--------|-------------|----------------|
| **Purpose** | Persists serializable data for process restoration | Retains arbitrary object instances across recreation |
| **Survives Process Death** | Yes | No |
| **Data Type** | Serializable data only | Any object (often non-serializable) |
| **Implementation** | `StateKeeperDispatcher` with `SerializableContainer` | `InstanceKeeperDispatcher` with key-value registry |
| **Lifecycle** | Bound to component lifecycle, saves on destroy | Bound to component lifecycle, destroys when component truly disposed |

## Understanding StateKeeper for Serializable State Persistence

**StateKeeper** is designed for state preservation—saving UI state, navigation stacks, or user input that must survive process death. It wraps a `StateKeeperDispatcher` that stores a `SerializableContainer`, ensuring data can be written to and restored from the Android saved state mechanism or equivalent platform implementations.

In [`ChildStateKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/ChildStateKeeper.kt), the library creates a child `StateKeeperDispatcher` and registers it with the parent's registry, enabling hierarchical state management where each component maintains isolated state keys.

```kotlin
class CounterComponent(
    componentContext: ComponentContext
) : ComponentContext by componentContext {

    private val stateKeeper = stateKeeper.child(key = "counter", lifecycle = lifecycle)

    private var count: Int = 0
        set(value) {
            field = value
            stateKeeper.register(key = "value", serializer = Int.serializer()) { value }
        }

    init {
        count = stateKeeper.consume(key = "value", serializer = Int.serializer())?.value ?: 0
    }

    fun increment() {
        count++
    }
}

```

## Understanding InstanceKeeper for Object Retention

**InstanceKeeper** serves a different purpose: instance retention. It holds references to arbitrary objects (often non-serializable) that should survive configuration changes but don't need to persist through process death. This mimics AndroidX `ViewModel` behavior, keeping heavy objects, coroutine scopes, or service handles in memory during recreation.

The implementation in [`ChildInstanceKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/ChildInstanceKeeper.kt) creates a child `InstanceKeeperDispatcher` that maintains a registry of `Instance` objects. When the component is finally destroyed (not just recreated), the dispatcher triggers `onDestroy()` on retained instances.

```kotlin
class CounterComponent(
    componentContext: ComponentContext
) : ComponentContext by componentContext {

    private val logic: CounterLogic = instanceKeeper.getOrCreate(key = "logic") {
        CounterLogic()
    }

    fun startCounting() = logic.start()
    fun stopCounting() = logic.stop()
}

class CounterLogic : InstanceKeeper.Instance {
    private var job: Job? = null

    fun start() {
        job = CoroutineScope(Dispatchers.Default).launch {
            while (isActive) {
                delay(250)
            }
        }
    }

    fun stop() {
        job?.cancel()
    }

    override fun onDestroy() {
        stop()
    }
}

```

## Key Implementation Files

Both mechanisms are implemented in the Decompose source code with clear separation of concerns:

- **ChildStateKeeper.kt**: Creates child `StateKeeperDispatcher` instances and registers them with the parent's state registry. Located at [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/ChildStateKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/ChildStateKeeper.kt).

- **ChildInstanceKeeper.kt**: Creates child `InstanceKeeperDispatcher` instances and manages their lifecycle attachment. Located at [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/instancekeeper/ChildInstanceKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/instancekeeper/ChildInstanceKeeper.kt).

- **ComponentContext.kt**: Defines the interface aggregating `LifecycleOwner`, `StateKeeperOwner`, and `InstanceKeeperOwner`, providing access to both mechanisms. Located at [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt).

## Summary

- **StateKeeper** persists serializable data across process death using `StateKeeperDispatcher` and `SerializableContainer`, ideal for UI state and navigation stacks.
- **InstanceKeeper** retains non-serializable object instances across configuration changes using `InstanceKeeperDispatcher`, suitable for heavy objects and coroutine scopes.
- Both are scoped per component via `ComponentContext`, with [`ChildStateKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/ChildStateKeeper.kt) and [`ChildInstanceKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/ChildInstanceKeeper.kt) handling hierarchical delegation.
- StateKeeper survives process death; InstanceKeeper does not.

## Frequently Asked Questions

### Can I use StateKeeper and InstanceKeeper together in the same component?

Yes. In fact, most production components use both mechanisms simultaneously. Use **StateKeeper** for serializable UI state that must survive process death, and **InstanceKeeper** for non-serializable heavy objects or coroutine scopes that only need to survive configuration changes.

### Does InstanceKeeper survive process death like StateKeeper?

No. **InstanceKeeper** only retains objects in memory during component recreation, such as Android configuration changes. When the process is killed and restored, InstanceKeeper-held objects are recreated from scratch, whereas StateKeeper-restored data is available immediately.

### How do I choose between StateKeeper and InstanceKeeper for my data?

Choose **StateKeeper** if your data is serializable (primitives, data classes with `@Serializable`, lists) and represents UI state the user expects to persist after backgrounding the app. Choose **InstanceKeeper** if your data is non-serializable (database connections, ViewModels, coroutine scopes) or expensive to recreate, and you only need it to survive configuration changes, not process death.

### Where are StateKeeper and InstanceKeeper implemented in the Decompose source code?

The hierarchical delegation logic resides in [`ChildStateKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/ChildStateKeeper.kt) and [`ChildInstanceKeeper.kt`](https://github.com/arkivanov/decompose/blob/main/ChildInstanceKeeper.kt) within the `decompose/src/commonMain/kotlin/com/arkivanov/decompose/` directory tree. The underlying dispatchers are defined in the Essenty library, which Decompose depends on for both `StateKeeperDispatcher` and `InstanceKeeperDispatcher` implementations.