# How Decompose Integrates with Jetpack Compose for UI Rendering

> Learn how Decompose integrates with Jetpack Compose for seamless UI rendering. Discover its use of Children composable for navigation state, state preservation, and animations.

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

---

**Decompose integrates with Jetpack Compose through the `Children` composable, which subscribes to `ChildStack` navigation state, preserves child state across configuration changes, and delegates rendering to user-provided content lambdas while supporting customizable `StackAnimation` transitions.**

Decompose is a component-oriented library developed by Arkadii Ivanov that isolates business logic, navigation, and state management from the UI layer. When you need to integrate Decompose with Jetpack Compose, the library provides dedicated extension modules that bridge its `ComponentContext`-based architecture with Compose's declarative rendering paradigm.

## Core Architecture Concepts

### ComponentContext and Lifecycle Bridging

At the heart of every Decompose component is `ComponentContext`, which holds lifecycle, saved-state, and instance state. When running on Android, you can use `JetpackComponentContext` from the `jetpack-component-context` module to bridge with Android Jetpack APIs.

In [`jetpack-component-context/src/commonMain/kotlin/com/arkivanov/decompose/jetpackcomponentcontext/JetpackComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/jetpack-component-context/src/commonMain/kotlin/com/arkivanov/decompose/jetpackcomponentcontext/JetpackComponentContext.kt), the interface extends `ComponentContext` and adds `LifecycleOwner` and `ViewModelStoreOwner` capabilities. This allows Decompose components to host Android `ViewModel` instances and observe Jetpack lifecycle events while remaining testable and platform-agnostic at the business logic layer.

### ChildStack as Navigation State

Navigation in Decompose is represented by `ChildStack`, defined in [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt). This immutable data structure contains:

- The currently active child component
- A back-stack of previously active configurations

The UI layer receives navigation updates through `Value<ChildStack<Config, Child>>`, Decompose's reactive observable type. Unlike direct Compose `State`, `Value` is platform-agnostic and manages subscription lifecycle automatically.

## The Children Composable

The primary integration point between Decompose and Jetpack Compose is the `Children` composable, located in [`extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt`](https://github.com/arkivanov/decompose/blob/main/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt).

This composable performs three critical functions:

1. **State Subscription**: Converts the `Value<ChildStack>` into a Compose `State` using `subscribeAsState()`
2. **State Preservation**: Uses `rememberSaveableStateHolder()` and `retainStates()` to maintain each child's `SavedStateHandle` across configuration changes
3. **Rendering Delegation**: Invokes a user-provided `content` lambda to render the active child, wrapping it in a `SaveableStateProvider`

```kotlin
// MyComponent.kt
class MyComponent(
    componentContext: ComponentContext,
) : ComponentContext by componentContext {

    private val _stack = mutableValue(
        ChildStack(Config.Home, HomeChild(componentContext))
    )
    val stack: Value<ChildStack<Config, Child>> = _stack

    fun openDetails(id: String) {
        _stack.value = ChildStack(
            Config.Details(id),
            DetailsChild(componentContext, id)
        )
    }

    fun goBack() {
        _stack.value = _stack.value.backStack.lastOrNull()
            ?.let { ChildStack(it.configuration, it.instance) }
            ?: _stack.value
    }

    sealed class Config { 
        object Home : Config() 
        data class Details(val id: String) : Config() 
    }
    
    sealed class Child {
        data class HomeChild(val ctx: ComponentContext) : Child()
        data class DetailsChild(val ctx: ComponentContext, val id: String) : Child()
    }
}

```

```kotlin
// MyScreen.kt
@Composable
fun MyScreen(component: MyComponent) {
    Children(
        stack = component.stack,
        animation = stackAnimation(animator = fade() + scale())
    ) { child ->
        when (val c = child.instance) {
            is MyComponent.Child.HomeChild -> 
                HomeContent(onItemClick = component::openDetails)
            is MyComponent.Child.DetailsChild -> 
                DetailsContent(id = c.id, onBack = component::goBack)
        }
    }
}

```

## Animating Navigation Transitions

### StackAnimation Interface

Decompose separates animation logic from UI rendering through the `StackAnimation` interface, defined in [`extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation.kt`](https://github.com/arkivanov/decompose/blob/main/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation.kt).

This functional interface receives the entire `ChildStack`, a `Modifier`, and a content lambda. Implementations determine how to animate between the previous and new active children. The `extensions-compose` module provides factory functions like `stackAnimation()` that combine multiple `StackAnimator` instances (such as `fade()` and `scale()`) into a single animation.

### Predictive Back Gesture Support

For Android 13+ predictive back gestures, Decompose provides `PredictiveBackParams` in [`extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackParams.kt`](https://github.com/arkivanov/decompose/blob/main/extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackParams.kt).

This data class connects the platform's predictive back handler with the component's navigation logic, enabling fluid animations that follow the user's finger gesture before the back action completes.

```kotlin
// SharedTransitionsContent.kt (from sample)
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalDecomposeApi::class)
@Composable
internal fun SharedTransitionsContent(
    component: SharedTransitionsComponent,
    modifier: Modifier = Modifier,
) {
    SharedTransitionLayout(modifier = modifier) {
        ChildStack(
            stack = component.stack,
            modifier = Modifier.fillMaxSize().background(Color.Black),
            animation = stackAnimation(
                animator = fade() + scale(),
                predictiveBackParams = {
                    PredictiveBackParams(
                        backHandler = component.backHandler,
                        onBack = component::onBack,
                        animatable = ::materialPredictiveBackAnimatable,
                    )
                },
            ),
        ) {
            when (val child = it.instance) {
                is SharedTransitionsComponent.Child.GalleryChild ->
                    GalleryContent(
                        component = child.component,
                        animatedVisibilityScope = this,
                        modifier = Modifier.fillMaxSize(),
                    )
                is SharedTransitionsComponent.Child.PhotoChild ->
                    PhotoContent(
                        component = child.component,
                        animatedVisibilityScope = this,
                        modifier = Modifier.fillMaxSize(),
                    )
            }
        }
    }
}

```

## Bridging with Android Jetpack APIs

When targeting Android, you can leverage `JetpackComponentContext` to integrate Decompose with Android-specific architecture components. This interface, defined in [`jetpack-component-context/src/commonMain/kotlin/com/arkivanov/decompose/jetpackcomponentcontext/JetpackComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/jetpack-component-context/src/commonMain/kotlin/com/arkivanov/decompose/jetpackcomponentcontext/JetpackComponentContext.kt), extends `ComponentContext` to provide `LifecycleOwner` and `ViewModelStoreOwner` capabilities.

This bridge allows Decompose components to host Android `ViewModel` instances that survive configuration changes while remaining compatible with Compose. The `extensions-compose` module provides a `viewModel { ... }` extension function that works seamlessly with `JetpackComponentContext`.

```kotlin
// CounterComponent.kt
class CounterComponent(
    componentContext: JetpackComponentContext,
) : JetpackComponentContext by componentContext {

    // Jetpack ViewModel that survives config changes
    private val vm: CounterViewModel = viewModel { CounterViewModel() }

    val count: State<Int> = vm.counter.collectAsState()
    fun inc() = vm.inc()
}

```

```kotlin
// CounterScreen.kt
@Composable
fun CounterScreen(component: CounterComponent) {
    val count by component.count
    Column {
        Text("Count = $count")
        Button(onClick = component::inc) { Text("Increment") }
    }
}

```

## Summary

- **Decompose** isolates business logic from UI through `ComponentContext`, while **Jetpack Compose** handles declarative rendering via the `Children` composable.
- The `Children` function in `extensions-compose` subscribes to `ChildStack` navigation state, automatically preserves child state using `rememberSaveableStateHolder()`, and delegates rendering to user-provided content lambdas.
- **StackAnimation** provides customizable transitions between navigation states, with support for Android 13+ predictive back gestures via `PredictiveBackParams`.
- **JetpackComponentContext** bridges Decompose with Android architecture components, allowing seamless use of `ViewModel` and lifecycle-aware APIs within Compose UI.

## Frequently Asked Questions

### How does Decompose preserve UI state across configuration changes in Jetpack Compose?

The `Children` composable uses `rememberSaveableStateHolder()` and `retainStates()` to maintain each child's `SavedStateHandle` during configuration changes. This mechanism mirrors Android's `ViewModel` behavior by retaining state in the `ChildStack` while the UI layer recomposes, ensuring that text field inputs, scroll positions, and other transient UI state survive rotation.

### Can I use Android Jetpack ViewModels with Decompose components?

Yes, when using `JetpackComponentContext` from the `jetpack-component-context` module, your component implements `ViewModelStoreOwner`. You can then use the `viewModel { ... }` extension from `extensions-compose` to create Android `ViewModel` instances that survive configuration changes while remaining compatible with Compose. This approach works seamlessly on Android while gracefully degrading on non-Android targets where `JetpackComponentContext` is unavailable.

### What is the difference between ChildStack and the Children composable?

`ChildStack`, defined in [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt), is an immutable data structure representing the navigation stack containing the active child and back-stack configurations. `Children`, located in [`extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt`](https://github.com/arkivanov/decompose/blob/main/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt), is the composable function that subscribes to a `Value<ChildStack>`, handles state preservation, applies animations, and renders the UI using a user-provided content lambda.

### How do I implement predictive back gestures with Decompose and Jetpack Compose?

Pass `PredictiveBackParams` to the `stackAnimation` factory, providing the `BackHandler` from your component and a `materialPredictiveBackAnimatable` instance. This configuration, available in the `extensions-compose-experimental` module, enables Android 13+ predictive back animations that follow the user's finger gesture before the navigation completes, while maintaining Decompose's navigation state management as demonstrated in the [`SharedTransitionsContent.kt`](https://github.com/arkivanov/decompose/blob/main/SharedTransitionsContent.kt) sample.