# How to Create Custom ComponentContext Implementations in Decompose

> Learn to create custom ComponentContext implementations in Decompose using Kotlin delegation. Enhance your app architecture with custom context wrappers for better control.

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

---

**To create a custom ComponentContext in Decompose, define an interface extending `GenericComponentContext<YourContext>`, implement it using Kotlin delegation to a standard `ComponentContext`, and provide a `ComponentContextFactory` that wraps child contexts with your implementation.**

When building complex applications with Decompose, the default `ComponentContext` may not provide all dependencies your components need, such as loggers or network clients. Learning how to create custom ComponentContext implementations in Decompose allows you to extend the framework's core context while maintaining full compatibility with navigation models like `childStack` and `childNavigation`. This guide walks through the architecture and implementation patterns found in the `arkivanov/decompose` repository.

## Understanding the ComponentContext Architecture

Decompose's context system is built on a generic foundation that separates the essential lifecycle and state management capabilities from specific component implementations. The architecture centers on delegation and factory patterns that allow seamless extension without breaking existing navigation contracts.

### Core Interfaces and Their Roles

The framework defines several key interfaces in `decompose/src/commonMain/kotlin/com/arkivanov/decompose/`:

- **`GenericComponentContext<T>`** – The generic base interface defined in [`GenericComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/GenericComponentContext.kt) that extends `LifecycleOwner`, `StateKeeperOwner`, `InstanceKeeperOwner`, and `BackHandlerOwner` from Essenty. It also inherits `ComponentContextFactoryOwner<T>`, exposing the `componentContextFactory` property required for creating child contexts.

- **`ComponentContext`** – The default concrete interface defined in [`ComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/ComponentContext.kt) that extends `GenericComponentContext<ComponentContext>`. This is what Decompose supplies by default when you create root components.

- **`ComponentContextFactory<T>`** – A functional interface defined in [`ComponentContextFactory.kt`](https://github.com/arkivanov/decompose/blob/main/ComponentContextFactory.kt) responsible for instantiating fresh context objects. Navigation models invoke this factory when spawning child contexts that are not attached to a parent.

- **`ComponentContextFactoryOwner<T>`** – Defined in [`ComponentContextFactoryOwner.kt`](https://github.com/arkivanov/decompose/blob/main/ComponentContextFactoryOwner.kt), this interface simply exposes the `componentContextFactory` property that navigation utilities query when building component hierarchies.

## Step-by-Step: Creating a Custom ComponentContext

To inject additional dependencies like loggers or API clients into your component tree, you create a custom context implementation following the delegation pattern documented in [`docs/component/custom-component-context.md`](https://github.com/arkivanov/decompose/blob/main/docs/component/custom-component-context.md).

### Step 1: Define the Custom Interface

Create an interface that extends `GenericComponentContext` parameterized with your custom type. Add any additional properties your components require.

```kotlin
import com.arkivanov.decompose.GenericComponentContext

// Custom interface extending the generic base
interface AppComponentContext : GenericComponentContext<AppComponentContext> {
    // Additional dependency available to all components using this context
    val logger: Logger
}

```

### Step 2: Implement with Kotlin Delegation

Create a concrete implementation class that receives a standard `ComponentContext` and delegates all Essenty owner interfaces to it. This preserves the lifecycle, state preservation, and back handling capabilities while adding your custom members.

```kotlin
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.backhandler.BackHandlerOwner
import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
import com.arkivanov.essenty.lifecycle.LifecycleOwner
import com.arkivanov.essenty.statekeeper.StateKeeperOwner

class DefaultAppComponentContext(
    private val componentContext: ComponentContext,
    override val logger: Logger,
) : AppComponentContext,
    // Delegate all Essenty owners to the supplied ComponentContext
    LifecycleOwner by componentContext,
    StateKeeperOwner by componentContext,
    InstanceKeeperOwner by componentContext,
    BackHandlerOwner by componentContext {

    // Expose the factory for creating child contexts of the same custom type
    override val componentContextFactory: ComponentContextFactory<AppComponentContext> =
        ComponentContextFactory { lifecycle, stateKeeper, instanceKeeper, backHandler ->
            // First create a plain ComponentContext using the original factory
            val plainContext = componentContext.componentContextFactory(
                lifecycle,
                stateKeeper,
                instanceKeeper,
                backHandler
            )
            // Wrap it with our custom implementation, preserving the logger
            DefaultAppComponentContext(plainContext, logger)
        }
}

```

### Step 3: Provide the ComponentContextFactory

The `componentContextFactory` property is crucial. When navigation models like `childStack` create child components, they invoke this factory. Your implementation must create a standard `ComponentContext` first, then wrap it with your custom class, ensuring that child components inherit the same additional capabilities (like the logger).

## Using Your Custom Context in Components

When constructing components, request your custom context type instead of the default `ComponentContext`. All navigation utilities will automatically supply the correct type through the factory defined above.

```kotlin
class MyFeatureComponent(
    private val context: AppComponentContext
) : Component {
    init {
        // Direct access to the custom logger
        context.logger.d("MyFeatureComponent created")
    }

    fun onSomeAction() {
        context.logger.d("Action triggered")
        // Access lifecycle, stateKeeper, etc., via the delegated members
    }
}

```

Because `DefaultAppComponentContext` implements `AppComponentContext` and properly exposes the `componentContextFactory`, functions like `childStack`, `childNavigation`, and `childContext` will automatically create child components with `AppComponentContext` instances that retain access to the logger.

## Key Source Files in the Decompose Repository

Understanding the following files helps when debugging or extending the context system:

- [`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) – Defines the default `ComponentContext` interface extending `GenericComponentContext<ComponentContext>`.

- [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/GenericComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GenericComponentContext.kt) – Provides the generic base that bundles Essenty owners and the factory contract.

- [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt) – Specifies the functional interface for instantiating contexts.

- [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactoryOwner.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactoryOwner.kt) – Exposes the factory property required by navigation models.

- [`docs/component/custom-component-context.md`](https://github.com/arkivanov/decompose/blob/main/docs/component/custom-component-context.md) – Official documentation with the pattern and sample code for creating custom contexts.

## Summary

- **Extend `GenericComponentContext<T>`** to define a custom context interface that retains Decompose's core capabilities while adding your own dependencies.

- **Use Kotlin delegation** (`by`) to delegate `LifecycleOwner`, `StateKeeperOwner`, `InstanceKeeperOwner`, and `BackHandlerOwner` to a standard `ComponentContext`, avoiding boilerplate.

- **Implement `componentContextFactory`** to wrap plain child contexts with your custom implementation, ensuring navigation utilities propagate your extended capabilities to child components.

- **Reference the source** in `decompose/src/commonMain/kotlin/com/arkivanov/decompose/` to understand the factory pattern and generic architecture.

## Frequently Asked Questions

### Why would I need a custom ComponentContext instead of using constructor injection?

While constructor injection works for direct dependencies, a custom `ComponentContext` is essential when using Decompose's navigation utilities like `childStack` or `childNavigation`. These functions automatically create child components using the `componentContextFactory`, so wrapping dependencies in the context ensures they propagate down the component tree without manual passing through every constructor.

### Does creating a custom ComponentContext break Decompose navigation?

No. As long as your custom context implements `GenericComponentContext<YourType>` and exposes a valid `componentContextFactory`, all navigation models work unchanged. The `childStack`, `childNavigation`, and `childContext` functions rely only on the `ComponentContextFactoryOwner` interface, which your implementation satisfies by delegating to the underlying context or providing your own factory.

### How do I access the custom context in child components?

Child components receive your custom context automatically when you use the factory pattern described above. Simply declare your custom interface type (e.g., `AppComponentContext`) as the constructor parameter for child components. When `childStack` creates these children, it invokes your `componentContextFactory`, which wraps the child context with your custom implementation, preserving access to extended properties like loggers or API clients.

### Can I add multiple custom dependencies to the same context?

Yes. You can add as many properties as needed to your custom interface (e.g., `val logger: Logger`, `val apiClient: ApiClient`, `val config: AppConfig`). Your implementation class receives all these dependencies via its constructor and exposes them through the interface. The `componentContextFactory` must then accept these dependencies and pass them down when wrapping child contexts, ensuring the entire component tree has access to the same shared services.