# Difference Between SlotNavigation and StackNavigation in Decompose

> Understand the difference between SlotNavigation and StackNavigation in Decompose. Learn how StackNavigation uses a back-stack for layered screens and SlotNavigation manages single optional configurations for exclusive UI eleme...

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

---

**Both navigation types implement the `NavigationSource` contract but manage fundamentally different state models: StackNavigation maintains a `List<C>` back-stack for layered screens, while SlotNavigation manages a single optional `C?` configuration for mutually exclusive UI elements like dialogs.**

In the [arkivanov/decompose](https://github.com/arkivanov/decompose) library, these two router implementations provide the foundation for navigation in Kotlin Multiplatform applications. Understanding the architectural distinction between managing a stack of configurations versus a single optional slot is essential for choosing the right navigation pattern for your UI components.

## Core Architectural Distinction

The primary difference lies in what each navigation type represents and navigates. According to the Decompose source code, both implement `NavigationSource<Event<*>>` and combine a navigator interface with event observation via a `Relay`, but they target different navigation paradigms.

**StackNavigation** works with a `StackNavigator<C>` that operates on a **list** of configurations (`List<C>`). This represents a traditional back-stack where the last element is the currently visible screen, and previous elements remain in the background. The stack must never be empty—there is always at least one active configuration.

**SlotNavigation** works with a `SlotNavigator<C>` that operates on a **single optional** configuration (`C?`). This represents a slot that either holds one active configuration or `null` (indicating no child is shown). Only one child can exist at a time, making it ideal for overlays and temporary UI.

## State Models and Transformers

The transformer signatures reveal the structural difference in how state mutations occur.

In [`router/stack/StackNavigator.kt`](https://github.com/arkivanov/decompose/blob/main/router/stack/StackNavigator.kt), the navigate method accepts:

```kotlin
transformer: (stack: List<C>) -> List<C>

```

This allows complex stack manipulations—pushing new elements, popping the last item, replacing the entire stack, or reordering configurations. The transformer receives the current stack and returns the new desired state.

In [`router/slot/SlotNavigator.kt`](https://github.com/arkivanov/decompose/blob/main/router/slot/SlotNavigator.kt), the transformer is simpler:

```kotlin
transformer: (configuration: C?) -> C?

```

This binary state (present or absent) limits operations to showing a new configuration (replacing the current one) or hiding it (returning `null`). You cannot maintain a history of previous slots; switching configurations destroys the previous child immediately.

## Lifecycle Handling and Behavior

Decompose handles component lifecycles differently for each navigation type based on their state models.

For **StackNavigation**, as implemented in [`DefaultStackNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultStackNavigation.kt), Decompose ensures only the **top** configuration’s component remains in the resumed state. When you push a new configuration, the previous top component enters the stopped state but remains in the back-stack. When you pop, the top component is destroyed and the previous one resumes.

For **SlotNavigation**, implemented in [`DefaultSlotNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/DefaultSlotNavigation.kt), the lifecycle is more absolute. When the transformer returns a new non-null configuration, Decompose creates and resumes that component, simultaneously destroying any previous configuration. When the transformer returns `null`, the current component is destroyed and no replacement occurs, leaving the slot empty.

## Practical Code Examples

### Stack Navigation: Pushing and Popping Screens

The following example from [`router/stack/StackNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/router/stack/StackNavigation.kt) demonstrates pushing a detail screen onto the stack:

```kotlin
val stackNavigation: StackNavigation<Config> = StackNavigation()

// Push a new screen
stackNavigation.navigate(
    transformer = { stack -> stack + Config.Detail(id = 42) },
    onComplete = { newStack, oldStack -> 
        println("Changed from $oldStack to $newStack") 
    }
)

```

To pop the top screen while ensuring the stack never empties:

```kotlin
stackNavigation.navigate(
    transformer = { stack -> 
        if (stack.size > 1) stack.dropLast(1) else stack 
    }
)

```

### Slot Navigation: Showing and Dismissing Dialogs

For modal dialogs or bottom sheets, use `SlotNavigation` from [`router/slot/SlotNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/router/slot/SlotNavigation.kt):

```kotlin
val slotNavigation: SlotNavigation<DialogConfig> = SlotNavigation()

// Show a dialog
slotNavigation.navigate(
    transformer = { _ -> DialogConfig.Alert(message = "Confirm?") }
)

```

To dismiss the dialog and return to the previous UI state:

```kotlin
slotNavigation.navigate(
    transformer = { _ -> null }
)

```

### Observing Navigation Events

Both implementations expose navigation events via the `NavigationSource` interface. You can subscribe to changes for logging or side effects:

```kotlin
stackNavigation.subscribe { event ->
    // Access the transformer or completion callback
    println("Stack navigation event received")
}

```

## Key Source Files

Understanding the implementation requires examining these specific files in the `decompose/src/commonMain/kotlin/com/arkivanov/decompose/` directory:

- **[`router/stack/StackNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/router/stack/StackNavigation.kt)** – Defines the `StackNavigation` interface and factory functions
- **[`router/stack/StackNavigator.kt`](https://github.com/arkivanov/decompose/blob/main/router/stack/StackNavigator.kt)** – Declares the `navigate` contract with `(List<C>) -> List<C>` transformers
- **[`router/stack/DefaultStackNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/router/stack/DefaultStackNavigation.kt)** – Implements event relaying to observers via `Relay<Event<C>>`
- **[`router/slot/SlotNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/router/slot/SlotNavigation.kt)** – Defines the `SlotNavigation` interface for single optional configurations
- **[`router/slot/SlotNavigator.kt`](https://github.com/arkivanov/decompose/blob/main/router/slot/SlotNavigator.kt)** – Provides the `(C?) -> C?` transformer-based navigation API
- **[`router/slot/DefaultSlotNavigation.kt`](https://github.com/arkivanov/decompose/blob/main/router/slot/DefaultSlotNavigation.kt)** – Forwards slot events to subscribed observers

Both default implementations push events into a `Relay`, enabling reactive navigation where UI components can observe changes without tight coupling to the navigator instances.

## Summary

- **StackNavigation** manages a `List<C>` back-stack suitable for hierarchical screen flows with history, where the last element represents the active screen.
- **SlotNavigation** manages a single `C?` configuration ideal for dialogs, modals, and mutually exclusive UI that either exists or is absent.
- **Transformers** differ in signature: stack transformers receive and return lists, while slot transformers handle nullable single values.
- **Lifecycle behavior** reflects the state model—stacks maintain stopped components in the back-stack, while slots destroy the previous component immediately upon change.
- Both implementations share the `NavigationSource<Event<*>>` architecture using `Relay` for event propagation, located in their respective `router/stack` and `router/slot` packages.

## Frequently Asked Questions

### When should I use SlotNavigation versus StackNavigation?

Use **SlotNavigation** for UI elements that overlay existing content without maintaining navigation history, such as alert dialogs, confirmation sheets, or modal bottom sheets. Use **StackNavigation** for primary app navigation where users expect back-button functionality and a history of visited screens, such as master-detail flows or onboarding sequences.

### Can I use both navigation types in the same component?

Yes. A single Decompose component can host multiple navigation instances. You typically use `StackNavigation` for the main content flow and `SlotNavigation` for auxiliary overlays. For example, a root component might manage a stack of screens while simultaneously managing a slot for a global error dialog or loading indicator.

### What happens if I return an empty list in a StackNavigation transformer?

Returning an empty list violates the contract of `StackNavigation`. The implementation expects at least one configuration to remain in the stack at all times. If you need to hide all content, use `SlotNavigation` instead, which explicitly supports `null` to represent the absence of any child component.

### How do the `onComplete` callbacks differ between the two types?

In `StackNavigation`, the `onComplete` callback receives both the new and old stack states: `(newStack: List<C>, oldStack: List<C>) -> Unit`. In `SlotNavigation`, it receives the new and old configurations: `(newConfig: C?, oldConfig: C?) -> Unit`. Both execute after Decompose processes the state change and updates the component hierarchy.