# How to Handle Back Button Presses with BackHandlerOwner in Decompose

> Master back button presses in Decompose using BackHandlerOwner. Learn to register lifecycle-aware back callbacks for seamless navigation stack integration. Get started today.

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

---

**Implement the `BackHandlerOwner` interface in your Decompose component and create a child handler using `backHandler.child()` to register lifecycle-aware back callbacks that integrate automatically with navigation stacks.**

Decompose, the Kotlin Multiplatform lifecycle-aware component library by Arkadii Ivanov, delegates back button handling to Essenty's back-handler API. Components that need to respond to system back presses must implement `BackHandlerOwner`, which exposes a back handler that chains to parent handlers via `ChildBackHandler`. This pattern ensures that back navigation remains predictable across nested component hierarchies in Android and Compose Multiplatform applications.

## Understanding BackHandlerOwner Delegation

In Decompose, component contexts delegate `BackHandlerOwner` to an internal back-handler instance. This delegation pattern makes the back handler available without manual wiring. For example, in [`DefaultJetpackComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultJetpackComponentContext.kt), the interface is delegated to the underlying generic component context:

```kotlin
class DefaultJetpackComponentContext<T : GenericComponentContext<T>>(
    delegate: T,
) : JetpackComponentContext,
    StateKeeperOwner by delegate,
    InstanceKeeperOwner by delegate,
    BackHandlerOwner by delegate   // ← Delegation happens here

```

By declaring your component as `BackHandlerOwner by componentContext`, you gain access to the `backHandler` property automatically. This handler connects to the parent's back chain, ensuring that child components receive back events only when they are active and enabled.

## Creating Child Back Handlers with Priority

To handle back presses within a component, obtain a **child back handler** from the parent using the `BackHandler.child` extension function. This creates a `ChildBackHandler` (defined in [`ChildBackHandler.kt`](https://github.com/arkivanov/decompose/blob/main/ChildBackHandler.kt)) that registers a `BackCallback` with the parent and mirrors its enabled state.

```kotlin
val childHandler = backHandler.child(
    lifecycle = lifecycle,               // Optional: ties enable-state to component lifecycle
    priority = BackCallback.PRIORITY_DEFAULT + 1
)

```

The [`DefaultChildBackHandler.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultChildBackHandler.kt) implementation manages the callback registration with the parent handler. When the child's `BackHandler` changes its enabled state—typically through a stack router—the `updateParentCallbackEnabledState()` method synchronizes this state with the parent's callback. Higher priority values ensure your handler receives the back event before lower-priority siblings.

## Wiring Back Handling to Navigation Stacks

When creating a stack router using `childStack`, pass the child back handler to the navigator. The router automatically enables or disables the back callback based on the current configuration's `isBackEnabled` flag.

In [`ChildrenFactory.kt`](https://github.com/arkivanov/decompose/blob/main/ChildrenFactory.kt), the router updates the callback state:

```kotlin
onStateChanged = { newState, oldState, isBackEnabled ->
    backCallback.isEnabled = isBackEnabled
    onStateChanged(newState, oldState)
}

```

This integration means the back button is only active when the current screen configuration explicitly allows back navigation. The `ChildrenNavigator` receives the `backHandler` parameter during initialization, creating a declarative link between the navigation state and system back events.

## Complete Implementation Examples

### Minimal Component with Manual Back Callback

For simple screens that don't use the stack router, register a callback directly on the child handler:

```kotlin
class SimpleScreen(
    componentContext: ComponentContext,
) : BackHandlerOwner by componentContext, ComponentContext by componentContext {

    private val backHandler = backHandler.child(
        lifecycle = lifecycle,
        priority = BackCallback.PRIORITY_DEFAULT + 1
    )

    init {
        backHandler.register(BackCallback(isEnabled = true) {
            onBackPressed()
        })
    }

    private fun onBackPressed() {
        println("Back pressed in SimpleScreen")
    }
}

```

### Stack Router with Automatic Back Handling

Components using `childStack` delegate back handling to the router. The [`CountersComponent.kt`](https://github.com/arkivanov/decompose/blob/main/CountersComponent.kt) interface declares `BackHandlerOwner`, while [`DefaultCountersComponent.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultCountersComponent.kt) implements the back logic:

```kotlin
internal class DefaultCountersComponent(
    componentContext: ComponentContext,
) : CountersComponent, ComponentContext by componentContext {

    private val navigation = StackNavigation<Config>()
    
    private val _stack = childStack(
        source = navigation,
        serializer = Config.serializer(),
        initialConfiguration = Config(index = 0, isBackEnabled = false),
        childFactory = ::child,
        backHandler = backHandler.child(priority = BackCallback.PRIORITY_DEFAULT + 1)  // Injected here
    )
    
    override val stack: Value<ChildStack<*, CounterComponent>> = _stack
    
    override fun onBackClicked() {
        navigation.pop()
    }
}

```

When the current configuration's `isBackEnabled` property is `true`, the router enables the `BackCallback`. Pressing the system back button triggers `onBackClicked`, which pops the navigation stack and updates the enabled state based on the new configuration.

### Overriding Handler Priority

To intercept back events before sibling components, increase the priority value:

```kotlin
val highPriorityHandler = backHandler.child(
    lifecycle = lifecycle,
    priority = BackCallback.PRIORITY_DEFAULT + 10   // Higher than default
)

highPriorityHandler.register(BackCallback(isEnabled = true) {
    // Handles back before lower-priority handlers
})

```

## Summary

- **Implement `BackHandlerOwner`** by delegating to your `ComponentContext` to access the back handler automatically.
- **Create child handlers** using `backHandler.child()` with optional lifecycle binding and priority settings.
- **Connect to navigation** by passing the child handler to `childStack`; the router manages `isEnabled` based on configuration flags.
- **Reference key files**: [`ChildBackHandler.kt`](https://github.com/arkivanov/decompose/blob/main/ChildBackHandler.kt) defines the interface, [`DefaultChildBackHandler.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultChildBackHandler.kt) handles parent registration, and [`ChildrenFactory.kt`](https://github.com/arkivanov/decompose/blob/main/ChildrenFactory.kt) wires the callback to navigation state changes.

## Frequently Asked Questions

### What is the difference between BackHandler and ChildBackHandler?

`BackHandler` is the public API from Essenty for registering back callbacks. `ChildBackHandler` (defined in [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/ChildBackHandler.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/ChildBackHandler.kt)) is Decompose's internal extension that creates a bridge between a component's local handler and its parent's handler chain. You typically interact with `BackHandler` through the `child()` extension function, which returns a `ChildBackHandler` instance.

### How does priority affect back button handling?

Priority determines the order in which registered callbacks receive back events. Higher integer values execute first. `BackCallback.PRIORITY_DEFAULT` provides the baseline; adding values (e.g., `+ 1` or `+ 10`) ensures your component handles the back press before siblings with lower priority. This is useful for modal dialogs or temporary UI that should intercept the back button before underlying navigation.

### Is the back handler automatically lifecycle-aware?

Yes. When creating a child handler via `backHandler.child(lifecycle = lifecycle)`, the handler automatically enables or disables its callback based on the component's lifecycle state. When the lifecycle is destroyed, the callback unregisters from the parent, preventing memory leaks and ensuring back events only reach active components.

### Can I use BackHandlerOwner in Compose Multiplatform projects?

Yes. While [`DefaultJetpackComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultJetpackComponentContext.kt) targets Android-specific contexts, the `BackHandlerOwner` interface and `ChildBackHandler` implementation reside in the common `decompose` module. This allows sharing back-button handling logic across Android, iOS, Desktop, and Web targets, with platform-specific integrations handling the actual system back button dispatch.