Repository: arkivanov/Decompose
Pull request #360 (2023-04-02 20:47:31)

[2.0] Introduced Child Slot, deprecated Child Overlay


Patch:
diff --git a/README.md b/README.md
index ac236f94..fc03d5dd 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,7 @@ Here are some key concepts of the library, more details can be found in the docu
 * [Component](https://arkivanov.github.io/Decompose/component/overview/) - every component represents a piece of logic with its own lifecycle, the UI is optional and is plugged externally
 * [ComponentContext](https://arkivanov.github.io/Decompose/component/overview/#componentcontext) - every component has its own [ComponentContext], which makes components lifecycle-aware and allows state preservation, instances retaining (aka AndroidX `ViewModel`) and back button handling
 * [Child Stack](https://arkivanov.github.io/Decompose/navigation/stack/overview/) - enables navigation between child components, nested navigation is also supported
-* [Child Overlay](https://arkivanov.github.io/Decompose/navigation/overlay/overview/) - provides navigation for the case when either one active component presented at a time, or none
+* [Child Slot](https://arkivanov.github.io/Decompose/navigation/slot/overview/) - allows only one child component at a time, or none
 * [Generic Navigation](https://arkivanov.github.io/Decompose/navigation/children/overview/) - provides a way to create your own custom navigation model, when none of the predefined models fit your needs
 * [Lifecycle](https://arkivanov.github.io/Decompose/component/lifecycle/) - provides a way to listen for lifecycle events in components
 * [StateKeeper](https://arkivanov.github.io/Decompose/component/state-preservation/) - makes it possible to preserve state or data in a component when it gets destroyed
diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api
index e2200e2d..3f148df0 100644
--- a/decompose/api/android/decompose.api
+++ b/decompose/api/android/decompose.api
@@ -180,6 +180,59 @@ public final class com/arkivanov/decompose/router/overlay/ValueExtKt {
 	public static final fun getOverlay (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created;
 }
 
+public final class com/arkivanov/decompose/router/slot/ChildSlot {
+	public fun <init> ()V
+	public fun <init> (Lcom/arkivanov/decompose/Child$Created;)V
+	public synthetic fun <init> (Lcom/arkivanov/decompose/Child$Created;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final fun component1 ()Lcom/arkivanov/decompose/Child$Created;
+	public final fun copy (Lcom/arkivanov/decompose/Child$Created;)Lcom/arkivanov/decompose/router/slot/ChildSlot;
+	public static synthetic fun copy$default (Lcom/arkivanov/decompose/router/slot/ChildSlot;Lcom/arkivanov/decompose/Child$Created;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/slot/ChildSlot;
+	public fun equals (Ljava/lang/Object;)Z
+	public final fun getChild ()Lcom/arkivanov/decompose/Child$Created;
+	public fun hashCode ()I
+	public fun toString ()Ljava/lang/String;
+}
+
+public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt {
+	public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+	public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+	public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
+	public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
+}
+
+public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/slot/SlotNavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator {
+}
+
+public final class com/arkivanov/decompose/router/slot/SlotNavigationKt {
+	public static final fun SlotNavigation ()Lcom/arkivanov/decompose/router/slot/SlotNavigation;
+}
+
+public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigationSource : com/arkivanov/decompose/router/children/NavigationSource {
+}
+
+public final class com/arkivanov/decompose/router/slot/SlotNavigationSource$Event {
+	public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
+	public synthetic fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final fun getOnComplete ()Lkotlin/jvm/functions/Function2;
+	public final fun getTransformer ()Lkotlin/jvm/functions/Function1;
+}
+
+public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigator {
+	public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
+}
+
+public final class com/arkivanov/decompose/router/slot/SlotNavigatorExtKt {
+	public static final fun activate (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
+	public static synthetic fun activate$default (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
+	public static final fun dismiss (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Lkotlin/jvm/functions/Function1;)V
+	public static synthetic fun dismiss$default (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
+	public static final fun navigate (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class com/arkivanov/decompose/router/slot/ValueExtKt {
+	public static final fun getChild (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created;
+}
+
 public final class com/arkivanov/decompose/router/stack/ChildStack {
 	public fun <init> (Lcom/arkivanov/decompose/Child$Created;Ljava/util/List;)V
 	public synthetic fun <init> (Lcom/arkivanov/decompose/Child$Created;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api
index 20c79416..aaec33d9 100644
--- a/decompose/api/jvm/decompose.api
+++ b/decompose/api/jvm/decompose.api
@@ -158,6 +158,59 @@ public final class com/arkivanov/decompose/router/overlay/ValueExtKt {
 	public static final fun getOverlay (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created;
 }
 
+public final class com/arkivanov/decompose/router/slot/ChildSlot {
+	public fun <init> ()V
+	public fun <init> (Lcom/arkivanov/decompose/Child$Created;)V
+	public synthetic fun <init> (Lcom/arkivanov/decompose/Child$Created;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final fun component1 ()Lcom/arkivanov/decompose/Child$Created;
+	public final fun copy (Lcom/arkivanov/decompose/Child$Created;)Lcom/arkivanov/decompose/router/slot/ChildSlot;
+	public static synthetic fun copy$default (Lcom/arkivanov/decompose/router/slot/ChildSlot;Lcom/arkivanov/decompose/Child$Created;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/slot/ChildSlot;
+	public fun equals (Ljava/lang/Object;)Z
+	public final fun getChild ()Lcom/arkivanov/decompose/Child$Created;
+	public fun hashCode ()I
+	public fun toString ()Ljava/lang/String;
+}
+
+public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt {
+	public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+	public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+	public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
+	public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
+}
+
+public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/slot/SlotNavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator {
+}
+
+public final class com/arkivanov/decompose/router/slot/SlotNavigationKt {
+	public static final fun SlotNavigation ()Lcom/arkivanov/decompose/router/slot/SlotNavigation;
+}
+
+public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigationSource : com/arkivanov/decompose/router/children/NavigationSource {
+}
+
+public final class com/arkivanov/decompose/router/slot/SlotNavigationSource$Event {
+	public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
+	public synthetic fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final fun getOnComplete ()Lkotlin/jvm/functions/Function2;
+	public final fun getTransformer ()Lkotlin/jvm/functions/Function1;
+}
+
+public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigator {
+	public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
+}
+
+public final class com/arkivanov/decompose/router/slot/SlotNavigatorExtKt {
+	public static final fun activate (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
+	public static synthetic fun activate$default (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
+	public static final fun dismiss (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Lkotlin/jvm/functions/Function1;)V
+	public static synthetic fun dismiss$default (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
+	public static final fun navigate (Lcom/arkivanov/decompose/router/slot/SlotNavigator;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class com/arkivanov/decompose/router/slot/ValueExtKt {
+	public static final fun getChild (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created;
+}
+
 public final class com/arkivanov/decompose/router/stack/ChildStack {
 	public fun <init> (Lcom/arkivanov/decompose/Child$Created;Ljava/util/List;)V
 	public synthetic fun <init> (Lcom/arkivanov/decompose/Child$Created;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
index 9193344e..915e5ca1 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
@@ -17,8 +17,8 @@ import com.arkivanov.essenty.statekeeper.consume
 /**
  * Initialised and manages a generic list of components. This is an API for custom navigation models.
  * Please consider the existing navigation models first:
- * [childStack][com.arkivanov.decompose.router.stack.childStack],
- * [childOverlay][com.arkivanov.decompose.router.overlay.childOverlay].
+ * [Child Stack][com.arkivanov.decompose.router.stack.childStack],
+ * [Child Slot][com.arkivanov.decompose.router.overlay.childSlot].
  *
  * The API is based around [NavState] and [ChildNavState] interfaces that should be implemented by
  * clients. [NavState] represents a persistent state of the navigation. It also holds a navigation
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt
index 70c27c3a..226afef8 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt
@@ -5,6 +5,13 @@ import com.arkivanov.decompose.Child
 /**
  * A state holder for `Child Overlay`.
  */
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "ChildSlot<C, T>",
+        "com.arkivanov.decompose.router.slot.ChildSlot",
+    ),
+)
 data class ChildOverlay<out C : Any, out T : Any>(
     val overlay: Child.Created<C, T>? = null,
 )
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt
index 673705e9..469936ec 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt
@@ -26,6 +26,13 @@ import kotlin.reflect.KClass
  * @param childFactory a factory function that creates new child instances.
  * @return an observable [Value] of [ChildOverlay].
  */
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "childSlot",
+        "com.arkivanov.decompose.router.slot.childSlot",
+    ),
+)
 fun <C : Parcelable, T : Any> ComponentContext.childOverlay(
     source: OverlayNavigationSource<C>,
     configurationClass: KClass<out C>,
@@ -59,6 +66,13 @@ fun <C : Parcelable, T : Any> ComponentContext.childOverlay(
  * @param childFactory a factory function that creates new child instances.
  * @return an observable [Value] of [ChildOverlay].
  */
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "childSlot",
+        "com.arkivanov.decompose.router.slot.childSlot",
+    ),
+)
 fun <C : Any, T : Any> ComponentContext.childOverlay(
     source: OverlayNavigationSource<C>,
     saveConfiguration: (C?) -> ParcelableContainer?,
@@ -90,6 +104,13 @@ fun <C : Any, T : Any> ComponentContext.childOverlay(
 /**
  * A convenience extension function for [ComponentContext.childOverlay].
  */
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "this.childSlot(source, key, initialConfiguration, persistent, handleBackButton, childFactory)",
+        "com.arkivanov.decompose.router.slot.childSlot",
+    ),
+)
 inline fun <reified C : Parcelable, T : Any> ComponentContext.childOverlay(
     source: OverlayNavigationSource<C>,
     key: String = "DefaultChildOverlay",
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt
index be2f296b..9af808e5 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt
@@ -9,6 +9,13 @@ interface OverlayNavigation<C : Any> : OverlayNavigator<C>, OverlayNavigationSou
  * Returns a default implementation of [OverlayNavigation].
  * Broadcasts navigation events to all subscribed observers.
  */
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "SlotNavigation<C>()",
+        "com.arkivanov.decompose.router.slot.SlotNavigation",
+    ),
+)
 @Suppress("FunctionName") // Factory function
 fun <C : Any> OverlayNavigation(): OverlayNavigation<C> =
     DefaultOverlayNavigation()
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt
index 5063922c..d1d28577 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt
@@ -7,6 +7,13 @@ import com.arkivanov.decompose.router.children.NavigationSource
  *
  * @see OverlayNavigator
  */
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "SlotNavigationSource",
+        "com.arkivanov.decompose.router.slot.SlotNavigationSource",
+    ),
+)
 interface OverlayNavigationSource<C : Any> : NavigationSource<OverlayNavigationSource.Event<C>> {
 
     class Event<C : Any>(
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt
index 1a345ea4..eee10172 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt
@@ -1,5 +1,12 @@
 package com.arkivanov.decompose.router.overlay
 
+@Deprecated(
+    message = "Please use Child Slot API",
+    replaceWith = ReplaceWith(
+        expression = "OverlayNavigator",
+        "com.arkivanov.decompose.router.slot.OverlayNavigator",
+    ),
+)
 interface OverlayNavigator<C : Any> {
 
     /**
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt
index 41ae5211..ff77abe8 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt
@@ -3,6 +3,7 @@ package com.arkivanov.decompose.router.overlay
 /**
  * A convenience method for [OverlayNavigator.navigate].
  */
+@Deprecated(message = "Please use Child Slot API")
 fun <C : Any> OverlayNavigator<C>.navigate(transformer: (configuration: C?) -> C?) {
     navigate(transformer = transformer, onComplete = { _, _ -> })
 }
@@ -14,6 +15,7 @@ fun <C : Any> OverlayNavigator<C>.navigate(transformer: (configuration: C?) -> C
  *
  * @param onComplete called when the navigation is finished (either synchronously or asynchronously).
  */
+@Deprecated(message = "Please use Child Slot API")
 fun <C : Any> OverlayNavigator<C>.activate(configuration: C, onComplete: () -> Unit = {}) {
     navigate(transformer = { configuration }, onComplete = { _, _ -> onComplete() })
 }
@@ -24,6 +26,7 @@ fun <C : Any> OverlayNavigator<C>.activate(configuration: C, onComplete: () -> U
  * @param onComplete called when the navigation is finished (either synchronously or asynchronously).
  * The `isSuccess` argument is `true` if there was an active overlay, `false` otherwise.
  */
+@Deprecated(message = "Please use Child Slot API")
 fun <C : Any> OverlayNavigator<C>.dismiss(onComplete: (isSuccess: Boolean) -> Unit = {}) {
     navigate(transformer = { null }, onComplete = { _, oldConfiguration -> onComplete(oldConfiguration != null) })
 }
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt
index cbb1c5b3..444c341a 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt
@@ -3,4 +3,6 @@ package com.arkivanov.decompose.router.overlay
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.value.Value
 
+@Suppress("DeprecatedCallableAddReplaceWith")
+@Deprecated(message = "Please use Child Slot API")
 val <C : Any, T : Any> Value<ChildOverlay<C, T>>.overlay: Child.Created<C, T>? get() = value.overlay
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlot.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlot.kt
new file mode 100644
index 00000000..24f41458
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlot.kt
@@ -0,0 +1,10 @@
+package com.arkivanov.decompose.router.slot
+
+import com.arkivanov.decompose.Child
+
+/**
+ * A state holder for `Child Slot`.
+ */
+data class ChildSlot<out C : Any, out T : Any>(
+    val child: Child.Created<C, T>? = null,
+)
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt
new file mode 100644
index 00000000..7383aa90
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt
@@ -0,0 +1,123 @@
+package com.arkivanov.decompose.router.slot
+
+import com.arkivanov.decompose.Child
+import com.arkivanov.decompose.ComponentContext
+import com.arkivanov.decompose.router.children.ChildNavState.Status
+import com.arkivanov.decompose.router.children.NavState
+import com.arkivanov.decompose.router.children.SimpleChildNavState
+import com.arkivanov.decompose.router.children.children
+import com.arkivanov.decompose.value.Value
+import com.arkivanov.essenty.parcelable.Parcelable
+import com.arkivanov.essenty.parcelable.ParcelableContainer
+import kotlin.reflect.KClass
+
+/**
+ * Initializes and manages a slot for one child component.
+ * The child component can be either active or dismissed (destroyed).
+ *
+ * @param source a source of navigation events.
+ * @param configurationClass a [KClass] of the component configurations.
+ * @param key a key of the slot, must be unique within the parent (hosting) component.
+ * @param initialConfiguration a component configuration that should be shown if there is
+ * no saved state, return `null` to show nothing.
+ * @param persistent determines whether the navigation state should pre preserved or not,
+ * default is `true`.
+ * @param handleBackButton determines whether the child component should be automatically dismissed
+ * on back button press or not, default is `false`.
+ * @param childFactory a factory function that creates new child instances.
+ * @return an observable [Value] of [ChildSlot].
+ */
+fun <C : Parcelable, T : Any> ComponentContext.childSlot(
+    source: SlotNavigationSource<C>,
+    configurationClass: KClass<out C>,
+    key: String = "DefaultChildSlot",
+    initialConfiguration: () -> C? = { null },
+    persistent: Boolean = true,
+    handleBackButton: Boolean = false,
+    childFactory: (configuration: C, ComponentContext) -> T,
+): Value<ChildSlot<C, T>> =
+    childSlot(
+        source = source,
+        saveConfiguration = { if (persistent) ParcelableContainer(it) else null },
+        restoreConfiguration = { it.consume(configurationClass) },
+        key = key,
+        initialConfiguration = initialConfiguration,
+        handleBackButton = handleBackButton,
+        childFactory = childFactory,
+    )
+
+/**
+ * Initializes and manages a slot for one child component.
+ * The child component can be either active or dismissed (destroyed).
+ *
+ * @param source a source of navigation events.
+ * @param key a key of the slot, must be unique within the parent (hosting) component.
+ * @param saveConfiguration a function that saves the provided configuration into [ParcelableContainer].
+ * @param restoreConfiguration a function that restores the configuration from the provided [ParcelableContainer].
+ * @param initialConfiguration a component configuration that should be shown if there is
+ * no saved state, return `null` to show nothing.
+ * @param handleBackButton determines whether the child component should be automatically dismissed
+ * on back button press or not, default is `false`.
+ * @param childFactory a factory function that creates new child instances.
+ * @return an observable [Value] of [ChildSlot].
+ */
+fun <C : Any, T : Any> ComponentContext.childSlot(
+    source: SlotNavigationSource<C>,
+    saveConfiguration: (C?) -> ParcelableContainer?,
+    restoreConfiguration: (ParcelableContainer) -> C?,
+    key: String = "DefaultChildSlot",
+    initialConfiguration: () -> C? = { null },
+    handleBackButton: Boolean = false,
+    childFactory: (configuration: C, ComponentContext) -> T,
+): Value<ChildSlot<C, T>> =
+    children(
+        source = source,
+        key = key,
+        initialState = { SlotNavState(configuration = initialConfiguration()) },
+        saveState = { saveConfiguration(it.configuration) },
+        restoreState = { SlotNavState(restoreConfiguration(it)) },
+        navTransformer = { state, event -> SlotNavState(configuration = event.transformer(state.configuration)) },
+        stateMapper = { _, children -> ChildSlot(child = children.firstOrNull() as? Child.Created?) },
+        onEventComplete = { event, newState, oldState -> event.onComplete(newState.configuration, oldState.configuration) },
+        backTransformer = { state ->
+            if (handleBackButton && (state.configuration != null)) {
+                { SlotNavState(configuration = null) }
+            } else {
+                null
+            }
+        },
+        childFactory = childFactory,
+    )
+
+/**
+ * A convenience extension function for [ComponentContext.childSlot].
+ */
+inline fun <reified C : Parcelable, T : Any> ComponentContext.childSlot(
+    source: SlotNavigationSource<C>,
+    key: String = "DefaultChildSlot",
+    noinline initialConfiguration: () -> C? = { null },
+    persistent: Boolean = true,
+    handleBackButton: Boolean = false,
+    noinline childFactory: (configuration: C, ComponentContext) -> T,
+): Value<ChildSlot<C, T>> =
+    childSlot(
+        source = source,
+        key = key,
+        configurationClass = C::class,
+        initialConfiguration = initialConfiguration,
+        persistent = persistent,
+        handleBackButton = handleBackButton,
+        childFactory = childFactory,
+    )
+
+private data class SlotNavState<out C : Any>(
+    val configuration: C?,
+) : NavState<C> {
+
+    override val children: List<SimpleChildNavState<C>> =
+        if (configuration == null) {
+            emptyList()
+        } else {
+            listOf(SimpleChildNavState(configuration = configuration, status = Status.ACTIVE))
+        }
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt
new file mode 100644
index 00000000..b0c2502e
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt
@@ -0,0 +1,24 @@
+package com.arkivanov.decompose.router.slot
+
+import com.arkivanov.decompose.Relay
+import com.arkivanov.decompose.router.slot.SlotNavigationSource.Event
+
+internal class DefaultSlotNavigation<C : Any> : SlotNavigation<C> {
+
+    private val relay = Relay<Event<C>>(isMainThreadCheckEnabled = true)
+
+    override fun navigate(
+        transformer: (configuration: C?) -> C?,
+        onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit,
+    ) {
+        relay.accept(Event(transformer, onComplete))
+    }
+
+    override fun subscribe(observer: (Event<C>) -> Unit) {
+        relay.subscribe(observer)
+    }
+
+    override fun unsubscribe(observer: (Event<C>) -> Unit) {
+        relay.unsubscribe(observer)
+    }
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt
new file mode 100644
index 00000000..80336258
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt
@@ -0,0 +1,14 @@
+package com.arkivanov.decompose.router.slot
+
+/**
+ * Represents [SlotNavigator] and [SlotNavigationSource] at the same time.
+ */
+interface SlotNavigation<C : Any> : SlotNavigator<C>, SlotNavigationSource<C>
+
+/**
+ * Returns a default implementation of [SlotNavigation].
+ * Broadcasts navigation events to all subscribed observers.
+ */
+@Suppress("FunctionName") // Factory function
+fun <C : Any> SlotNavigation(): SlotNavigation<C> =
+    DefaultSlotNavigation()
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt
new file mode 100644
index 00000000..3d12780c
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt
@@ -0,0 +1,16 @@
+package com.arkivanov.decompose.router.slot
+
+import com.arkivanov.decompose.router.children.NavigationSource
+
+/**
+ * Represents a source of navigation events for `Child Slot`.
+ *
+ * @see SlotNavigator
+ */
+interface SlotNavigationSource<C : Any> : NavigationSource<SlotNavigationSource.Event<C>> {
+
+    class Event<C : Any>(
+        val transformer: (configuration: C?) -> C?,
+        val onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit = { _, _ -> },
+    )
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigator.kt
new file mode 100644
index 00000000..2591e0c8
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigator.kt
@@ -0,0 +1,30 @@
+package com.arkivanov.decompose.router.slot
+
+interface SlotNavigator<C : Any> {
+
+    /**
+     * Transforms the current configuration into a new one. Configuration `null` means that the
+     * component is not shown.
+     *
+     * During the navigation process, the `Child Slot` compares the new configuration with
+     * the previous one. The `Child Slot` ensures that the current component is resumed, and a
+     * dismissed component is destroyed.
+     *
+     * The `Child Slot` usually performs the navigation synchronously, which means that by the time
+     * the `navigate` method returns, the navigation is finished and all component lifecycles are
+     * moved into required states. However, the navigation is performed asynchronously in case of
+     * recursive invocations - e.g. `dismiss` is called from `onResume` lifecycle callback of a
+     * component being shown. All recursive invocations are queued and performed one by one once
+     * the current navigation is finished.
+     *
+     * Should be called on the main thread.
+     *
+     * @param transformer transforms the current configuration to a new one, `null` means that the
+     * component is not shown.
+     * @param onComplete called when the navigation is finished (either synchronously or asynchronously).
+     */
+    fun navigate(
+        transformer: (configuration: C?) -> C?,
+        onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit,
+    )
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt
new file mode 100644
index 00000000..f5d53f67
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt
@@ -0,0 +1,32 @@
+package com.arkivanov.decompose.router.slot
+
+/**
+ * A convenience method for [SlotNavigator.navigate].
+ */
+fun <C : Any> SlotNavigator<C>.navigate(transformer: (configuration: C?) -> C?) {
+    navigate(transformer = transformer, onComplete = { _, _ -> })
+}
+
+/**
+ * Activates a child component represented by the provided [configuration],
+ * and dismisses (destroys) any currently active component. Does nothing if the provided [configuration]
+ * is equal to the currently active one.
+ *
+ * @param onComplete called when the navigation is finished (either synchronously or asynchronously).
+ */
+fun <C : Any> SlotNavigator<C>.activate(configuration: C, onComplete: () -> Unit = {}) {
+    navigate(transformer = { configuration }, onComplete = { _, _ -> onComplete() })
+}
+
+/**
+ * Dismisses (destroys) the currently active child component, if any.
+ *
+ * @param onComplete called when the navigation is finished (either synchronously or asynchronously).
+ * The `isSuccess` argument is `true` if there was an active child component, `false` otherwise.
+ */
+fun SlotNavigator<*>.dismiss(onComplete: (isSuccess: Boolean) -> Unit = {}) {
+    navigate(
+        transformer = { null },
+        onComplete = { _, oldConfiguration -> onComplete(oldConfiguration != null) },
+    )
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ValueExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ValueExt.kt
new file mode 100644
index 00000000..f64a5770
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ValueExt.kt
@@ -0,0 +1,6 @@
+package com.arkivanov.decompose.router.slot
+
+import com.arkivanov.decompose.Child
+import com.arkivanov.decompose.value.Value
+
+val <C : Any, T : Any> Value<ChildSlot<C, T>>.child: Child.Created<C, T>? get() = value.child
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt
index 8d11352b..63740fb4 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt
@@ -23,8 +23,8 @@ import kotlin.reflect.KClass
  * @param key a key of the stack, must be unique if there are multiple stacks in the same component.
  * @param persistent determines whether the navigation state should pre preserved or not,
  * default is `true`.
- * @param handleBackButton determines whether the overlay should be automatically dismissed
- * on back button press or not, default is `false`.
+ * @param handleBackButton determines whether the stack should be automatically popped on back button press or not,
+ * default is `false`.
  * @param childFactory a factory function that creates new child instances.
  * @return an observable [Value] of [ChildStack].
  */
@@ -74,8 +74,8 @@ fun <C : Parcelable, T : Any> ComponentContext.childStack(
  * If `null` is returned then [initialStack] is used instead.
  * The restored stack must have the same amount of configurations and in the same order.
  * @param key a key of the stack, must be unique if there are multiple stacks in the same component.
- * @param handleBackButton determines whether the overlay should be automatically dismissed
- * on back button press or not, default is `false`.
+ * @param handleBackButton determines whether the stack should be automatically popped on back button press or not,
+ * default is `false`.
  * @param childFactory a factory function that creates new child instances.
  * @return an observable [Value] of [ChildStack].
  */
diff --git a/docs/component/back-button.md b/docs/component/back-button.md
index ef9a876e..f11fa89d 100644
--- a/docs/component/back-button.md
+++ b/docs/component/back-button.md
@@ -6,7 +6,7 @@ Some devices (e.g. Android) have hardware back buttons. A very common use case i
 
 `Child Stack` can automatically navigate back when the back button is pressed. All you need to do is to supply the `handleBackButton=true` argument when you initialize the `ChildStack`. Please see the [Child Stack](/Decompose/navigation/stack/overview/) documentation page for more information.
 
-Similarly, `Child Overlay` can automatically dismiss the child component when the back button is pressed. see the [Child Overlay](/Decompose/navigation/overlay/overview/) documentation page for more information.
+Similarly, `Child Slot` can automatically dismiss the child component when the back button is pressed. see the [Child Slot](/Decompose/navigation/slot/overview/) documentation page for more information.
 
 ## Manual back button handling
 
diff --git a/docs/component/custom-component-context.md b/docs/component/custom-component-context.md
index e692c0b3..0b7e8669 100644
--- a/docs/component/custom-component-context.md
+++ b/docs/component/custom-component-context.md
@@ -25,4 +25,4 @@ class DefaultAppComponentContext(
 ## Navigation with custom ComponentContext
 
 - [Using Child Stack](../navigation/stack/component-context.md)
-- [Using Child Overlay](../navigation/overlay/component-context.md)
+- [Using Child Slot](../navigation/slot/component-context.md)
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index e0bea476..fac05370 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -4,7 +4,7 @@ Decompose provides a number of modules, they are all published to Maven Central
 
 ## The main Decompose module
 
-The main functionality is provided by the `decompose` module. It contains some core features like [ComponentContext](/Decompose/component/overview/#componentcontext), [Child Stack](/Decompose/navigation/stack/overview/), [Child Overlay](/Decompose/navigation/overlay/overview/), etc.
+The main functionality is provided by the `decompose` module. It contains some core features like [ComponentContext](/Decompose/component/overview/#componentcontext), [Child Stack](/Decompose/navigation/stack/overview/), [Child Slot](/Decompose/navigation/slot/overview/), etc.
 
 ### Gradle setup
 
diff --git a/docs/navigation/children/overview.md b/docs/navigation/children/overview.md
index 8f6fa130..71605aca 100644
--- a/docs/navigation/children/overview.md
+++ b/docs/navigation/children/overview.md
@@ -1,6 +1,6 @@
 # Generic Navigation
 
-The `Generic Navigation` can be used to create custom navigation models, when none of the predefined models fit your needs. It offers a flexible API and allows you to create almost any kind of navigation. Please check out [Child Stack](../stack/overview.md) and [Child Overlay](../overlay/overview.md) before using the `Generic Navigation`.
+The `Generic Navigation` can be used to create custom navigation models, when none of the predefined models fit your needs. It offers a flexible API and allows you to create almost any kind of navigation. Please check out [Child Stack](../stack/overview.md) and [Child Slot](../slot/overview.md) before using the `Generic Navigation`.
 
 The API is based around [NavState](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavState.kt) and [ChildNavState](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt) interfaces that should be implemented by clients. `NavState` represents a persistent state of the navigation. It also holds a navigation state for each child - `ChildNavState`. Both `NavState` and `ChildNavState` must be immutable, and correctly implement `equals` and `hashCode` methods (or just be data classes). There must be no duplicated (by equality) `ChildNavState.configuration` within a `NavState`.
 
@@ -68,7 +68,7 @@ The `children` function returns an observable `Value` of the resulting children
 
 ## Examples
 
-Both [Child Stack](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt) and [Child Overlay](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt) are implemented using the `Generic Navigation`. Please refer to their source code for implementation details.
+Both [Child Stack](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt) and [Child Slot](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt) are implemented using the `Generic Navigation`. Please refer to their source code for implementation details.
 
 ### Sample project
 
diff --git a/docs/navigation/overlay/navigation.md b/docs/navigation/overlay/navigation.md
deleted file mode 100644
index a484d1d0..00000000
--- a/docs/navigation/overlay/navigation.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Navigation with Child Overlay
-
-## The OverlayNavigator
-
-All navigation in `Child Overlay` is performed using the [`OverlayNavigator`](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt) interface, which is extended by the `OverlayNavigation` interface. 
-
-`OverlayNavigator` contains the `navigate` method with the following arguments:
-
-- `transformer` - converts the current configuration (if any) into a new one or `null`.
-- `onComplete` - called when navigation is finished.
-
-There is also `navigate` extension function without the `onComplete` callback, for convenience.
-
-```kotlin title="Creating the navigation"
-val navigation = OverlayNavigation<DialogConfig>()
-```
-
-### The navigation process
-
-During the navigation process, `Child Overlay` compares the new configuration with the previous one. If both are the same, then no navigation is performed. Otherwise, the currently active component is destroyed (if any), and a new one is activated (if the new configuration is not `null`).  
-
-`Child Overlay` usually performs the navigation synchronously, which means that by the time the `navigate` method returns, the navigation is finished and all component lifecycles are moved into required states. However, the navigation is performed asynchronously in case of recursive invocations - e.g. `dismiss` is called from `onResume` lifecycle callback of a component being activated. All recursive invocations are queued and performed one by one once the current navigation is finished.
-
-## OverlayNavigator extension functions
-
-There are `OverlayNavigator` [extension functions](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt) to simplify the navigation.
-
-### activate
-
-Activates a component with the provided `Configuration` (if not `null`). Any currently active component is destroyed.
-
-```kotlin
-navigation.activate(DialogConfig(title = "Some title"))
-```
-
-### dismiss
-
-Destroys the currently active component, if any.
-
-```kotlin
-navigation.dismiss()
-```
diff --git a/docs/navigation/overlay/overview.md b/docs/navigation/overlay/overview.md
deleted file mode 100644
index 33a40ae8..00000000
--- a/docs/navigation/overlay/overview.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# Child Overlay overview
-
-## The Child Overlay
-
-`Child Overlay` is a navigation model with either one active component at a time, or none. In other words, each `Child Overlay` allows to activate a child component, replace with another child component, or dismiss when not needed. It is possible to have more than one `Child Overlay` in a component, nested overlays are also supported.
-
-The `Child Overlay` navigation consists of two main entities:
-
-- [ChildOverlay](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt) - a simple data class that holds the currently active child, if any.
-- [OverlayNavigation](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt) - an interface that accepts navigation commands and forwards them to all subscribed observers.
-
-### Component Configurations
-
-Each component created and managed by the `Child Overlay` has a configuration, please read the documentation about [child configurations](/Decompose/navigation/overview/#component-configurations-and-child-factories). 
-
-### Initializing the Child Overlay
-
-There are three steps to initialize the `Child Overlay`:
-
-- Create a new instance of `OverlayNavigation` and assign it to a variable or a property.
-- Initialize the `Child Overlay` using the `ComponentContext#childOverlay` extension function and pass `OverlayNavigation` into it along with other arguments.
-- The `childOverlay` function returns `Value<ChildOverlay>` that can be observed in the UI. Assign the returned `Value` to another property or a variable.
-
-## Example
-
-Here is a very basic example of a child overlay:
-
-```kotlin title="Dialog component"
-interface DialogComponent {
-
-    fun onDismissClicked()
-}
-
-class DefaultDialogComponent(
-    private val componentContext: ComponentContext,
-    private val message: String,
-    private val onDismissed: () -> Unit,
-) : DialogComponent, ComponentContext by componentContext {
-
-    override fun onDismissClicked() {
-        onDismissed()
-    }
-}
-```
-
-```kotlin title="Root component"
-interface RootComponent {
-
-    val dialog: Value<ChildOverlay<*, DialogComponent>>
-}
-
-class DefaultRootComponent(
-    componentContext: ComponentContext,
-) : RootComponent, ComponentContext by componentContext {
-
-    private val dialogNavigation = OverlayNavigation<DialogConfig>()
-
-    private val _dialog =
-        childOverlay(
-            source = dialogNavigation,
-            // persistent = false, // Disable navigation state saving, if needed
-            handleBackButton = true, // Close the dialog on back button press
-        ) { config, componentContext ->
-            DefaultDialogComponent(
-                componentContext = componentContext,
-                message = config.message,
-                onDismissed = dialogNavigation::dismiss,
-            )
-        }
-
-    override val dialog: Value<ChildOverlay<*, DialogComponent>> = _dialog
-
-    private fun showDialog(message: String) {
-        dialogNavigation.activate(DialogConfig(message = message))
-    }
-
-    @Parcelize
-    private data class DialogConfig(
-        val message: String,
-    ) : Parcelable
-}
-```
-
-## Multiple Child Overlays in a component
-
-When multiple `Child Overlays` are used in one component, each such `Child Overlay` must have a unique key associated. The keys are required to be unique only within the parent (hosting) component, so it is ok for different components to have `Child Overlays` with same keys. An exception will be thrown if multiple `Child Overlays` with the same key are detected in a component.
-
-```kotlin title="Two Child Overlays in one component"
-class Root(
-    componentContext: ComponentContext
-) : ComponentContext by componentContext {
-
-    private val topNavigation = OverlayNavigation<TopConfig>()
-    
-    private val topOverlay =
-        childOverlay<TopConfig, TopChild>(
-            source = topNavigation,
-            key = "TopOverlay",
-            // Omitted code
-        )
-
-    private val bottomNavigation = OverlayNavigation<BottomConfig>()
-    
-    private val bottomOverlay =
-        childOverlay<BottomConfig, BottomChild>(
-            source = bottomNavigation,
-            key = "BottomOverlay",
-            // Omitted code
-        )
-}
-```
diff --git a/docs/navigation/overview.md b/docs/navigation/overview.md
index 52f30e6b..9232b4a6 100644
--- a/docs/navigation/overview.md
+++ b/docs/navigation/overview.md
@@ -5,11 +5,13 @@ Decompose provides the ability to create [permanent child components](/Decompose
 Currently, Decompose provides two predefined navigation models:
 
 - [Child Stack](/Decompose/navigation/stack/overview/) - prefer this way if you need to organize child components in a stack and navigate between them.
-- [Child Overlay](/Decompose/navigation/overlay/overview/) - prefer this way if you need to activate-dismiss a child component.
+- [Child Slot](/Decompose/navigation/slot/overview/) - prefer this way if you need to activate-dismiss one child component at a time.
 
 If none of this fit your needs, Decompose introduces [Generic Navigation](https://arkivanov.github.io/Decompose/navigation/children/overview/) that can be used to create your own custom navigation models.
 It offers a flexible API and allows you to create almost any kind of navigation.
 
+It is possible to have more than one navigation model in a parent component. Make sure that you supplied different keys (the `key` argument) if you have two or more navigation models of the same kind (e.g. you have two `Child Stacks` in one parent component).
+
 ## Component configurations and child factories
 
 The term `Configuration` is widely used in Decompose navigation. A configuration is a persistent class that represents a child component and contains all its arguments (not dependencies). Decompose automatically persists child configurations using [StateKeeper](/Decompose/component/state-preservation/), so child components are automatically recreated on events like Android configuration changes, process death, etc.
diff --git a/docs/navigation/overlay/component-context.md b/docs/navigation/slot/component-context.md
similarity index 54%
rename from docs/navigation/overlay/component-context.md
rename to docs/navigation/slot/component-context.md
index 5600a7d9..fa7e03b8 100644
--- a/docs/navigation/overlay/component-context.md
+++ b/docs/navigation/slot/component-context.md
@@ -1,22 +1,20 @@
-# Child Overlay with custom `ComponentContext`
+# Child Slot with custom `ComponentContext`
 
 Custom `ComponentContext` allows passing extra data and functionality to every child component. See [Custom ComponentContext](../../component/custom-component-context.md) page for more information about creating custom `AppComponentContext`.
 
-In order to pass custom component context like `AppComponentContext` to child overlay components, make an extension function on your `AppComponentContext` interface. This custom extension function will initialize the `Child Overlay` and provide every child an `AppComponentContext`.
+In order to pass custom component context (like `AppComponentContext`) to child slot components, make an extension function on your `AppComponentContext` interface. This custom extension function will initialize the `Child Slot` and provide every child an `AppComponentContext`.
 
 ```kotlin
-fun <C : Parcelable, T : Any> AppComponentContext.appChildOverlay(
-    source: OverlayNavigationSource<C>,
-    initialConfiguration: () -> C?,
-    configurationClass: KClass<out C>,
-    key: String = "DefaultOverlay",
+inline fun <reified C : Parcelable, T : Any> AppComponentContext.appChildOverlay(
+    source: SlotNavigationSource<C>,
+    noinline initialConfiguration: () -> C? = { null },
+    key: String = "DefaultSlot",
     handleBackButton: Boolean = false,
     persistent: Boolean = false,
-    childFactory: (configuration: C, AppComponentContext) -> T
-): Value<ChildOverlay<C, T>> =
-    childOverlay(
+    noinline childFactory: (configuration: C, AppComponentContext) -> T
+): Value<ChildSlot<C, T>> =
+    childSlot(
         source = source,
-        configurationClass = configurationClass,
         key = key,
         handleBackButton = handleBackButton,
         initialConfiguration = initialConfiguration,
@@ -26,47 +24,27 @@ fun <C : Parcelable, T : Any> AppComponentContext.appChildOverlay(
             configuration,
             DefaultAppComponentContext(
                 componentContext = componentContext,
-                bundleId,
-                settings,
-                boarding
+                // Additional dependencies here
             )
         )
     }
-
-inline fun <reified C : Parcelable, T : Any> AppComponentContext.appChildStack(
-    source: OverlayNavigationSource<C>,
-    noinline  initialConfiguration: () -> C?,
-    key: String = "DefaultOverlay",
-    handleBackButton: Boolean = false,
-    persistent: Boolean = false,
-    noinline childFactory: (configuration: C, AppComponentContext) -> T
-): Value<ChildOverlay<C, T>> =
-    appChildOverlay(
-        source = source,
-        initialConfiguration = initialConfiguration,
-        configurationClass = C::class,
-        key = key,
-        handleBackButton = handleBackButton,
-        persistent = persistent,
-        childFactory = childFactory,
-    )
 ```
 
 Finally, in your components you can use the new extension function that will utilize the custom `AppComponentContext`.
 
 ```kotlin
 interface RootComponent {
-    val dialog: Value<ChildOverlay<*, DialogComponent>>
+    val dialog: Value<ChildSlot<*, DialogComponent>>
 }
 
 class DefaultRootComponent(
     componentContext: AppComponentContext,
 ) : RootComponent, AppComponentContext by componentContext {
 
-    private val dialogNavigation = OverlayNavigation<DialogConfig>()
+    private val dialogNavigation = SlotNavigation<DialogConfig>()
 
     private val _dialog =
-        appChildOverlay(
+        appChildSlot(
             source = dialogNavigation,
             // persistent = false, // Disable navigation state saving, if needed
             handleBackButton = true, // Close the dialog on back button press
@@ -78,7 +56,7 @@ class DefaultRootComponent(
             )
         }
 
-    override val dialog: Value<ChildOverlay<*, DialogComponent>> = _dialog
+    override val dialog: Value<ChildSlot<*, DialogComponent>> = _dialog
 
     private fun showDialog(message: String) {
         dialogNavigation.activate(DialogConfig(message = message))
diff --git a/docs/navigation/slot/navigation.md b/docs/navigation/slot/navigation.md
new file mode 100644
index 00000000..c63bafeb
--- /dev/null
+++ b/docs/navigation/slot/navigation.md
@@ -0,0 +1,42 @@
+# Navigation with Child Slot
+
+## The SlotNavigator
+
+All navigation in `Child Slot` is performed using the [`SlotNavigator`](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigator.kt) interface, which is extended by the `SlotNavigation` interface. 
+
+`SlotNavigator` contains the `navigate` method with the following arguments:
+
+- `transformer` - converts the current configuration (if any) into a new one or `null`.
+- `onComplete` - called when navigation is finished.
+
+There is also `navigate` extension function without the `onComplete` callback, for convenience.
+
+```kotlin title="Creating the navigation"
+val navigation = SlotNavigation<DialogConfig>()
+```
+
+### The navigation process
+
+During the navigation process, `Child Slot` compares the new configuration with the previous one. If both are the same, then no navigation is performed. Otherwise, the currently active component is destroyed (if any), and a new one is activated (if the new configuration is not `null`).  
+
+`Child Slot` usually performs the navigation synchronously, which means that by the time the `navigate` method returns, the navigation is finished and all component lifecycles are moved into required states. However, the navigation is performed asynchronously in case of recursive invocations - e.g. `dismiss` is called from `onResume` lifecycle callback of a component being activated. All recursive invocations are queued and performed one by one once the current navigation is finished.
+
+## SlotNavigator extension functions
+
+There are `SlotNavigator` [extension functions](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt) to simplify the navigation.
+
+### activate
+
+Activates a component with the provided `Configuration` (if not `null`). Any currently active component is destroyed.
+
+```kotlin
+navigation.activate(DialogConfig(title = "Some title"))
+```
+
+### dismiss
+
+Destroys the currently active component, if any.
+
+```kotlin
+navigation.dismiss()
+```
diff --git a/docs/navigation/slot/overview.md b/docs/navigation/slot/overview.md
new file mode 100644
index 00000000..9afdf73a
--- /dev/null
+++ b/docs/navigation/slot/overview.md
@@ -0,0 +1,113 @@
+# Child Slot overview
+
+## The Child Slot
+
+`Child Slot` is a navigation model that allows only one child component at a time, or none. In other words, each `Child Slot` allows to activate a child component, replace with another child component, or dismiss when not needed. It is possible to have more than one `Child Slot` in a component, nested slots are also supported.
+
+The most common use cases include but not limited to displaying dialogs, drawers, bottom sheets, and just changing the visibility of some views. It's not necessarily something that overlays the parent component.
+
+The `Child Slot` navigation consists of two main entities:
+
+- [ChildSlot](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlot.kt) - a simple data class that holds the currently active child, if any.
+- [SlotNavigation](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt) - an interface that accepts navigation commands and forwards them to all subscribed observers.
+
+### Component Configurations
+
+Each component created and managed by the `Child Slot` has a configuration, please read the documentation about [child configurations](/Decompose/navigation/overview/#component-configurations-and-child-factories). 
+
+### Initializing the Child Slot
+
+There are three steps to initialize the `Child Slot`:
+
+- Create a new instance of `SlotNavigation` and assign it to a variable or a property.
+- Initialize the `Child Slot` using the `ComponentContext#childSlot` extension function and pass `SlotNavigation` into it along with other arguments.
+- The `childSlot` function returns `Value<ChildSlot>` that can be observed in the UI. Assign the returned `Value` to another property or a variable.
+
+## Example
+
+Here is a very basic example of a child slot:
+
+```kotlin title="Dialog component"
+interface DialogComponent {
+
+    fun onDismissClicked()
+}
+
+class DefaultDialogComponent(
+    private val componentContext: ComponentContext,
+    private val message: String,
+    private val onDismissed: () -> Unit,
+) : DialogComponent, ComponentContext by componentContext {
+
+    override fun onDismissClicked() {
+        onDismissed()
+    }
+}
+```
+
+```kotlin title="Root component"
+interface RootComponent {
+
+    val dialog: Value<ChildSlot<*, DialogComponent>>
+}
+
+class DefaultRootComponent(
+    componentContext: ComponentContext,
+) : RootComponent, ComponentContext by componentContext {
+
+    private val dialogNavigation = SlotNavigation<DialogConfig>()
+
+    private val _dialog =
+        childSlot(
+            source = dialogNavigation,
+            // persistent = false, // Disable navigation state saving, if needed
+            handleBackButton = true, // Close the dialog on back button press
+        ) { config, componentContext ->
+            DefaultDialogComponent(
+                componentContext = componentContext,
+                message = config.message,
+                onDismissed = dialogNavigation::dismiss,
+            )
+        }
+
+    override val dialog: Value<ChildSlot<*, DialogComponent>> = _dialog
+
+    private fun showDialog(message: String) {
+        dialogNavigation.activate(DialogConfig(message = message))
+    }
+
+    @Parcelize
+    private data class DialogConfig(
+        val message: String,
+    ) : Parcelable
+}
+```
+
+## Multiple Child Slots in a component
+
+When multiple `Child Slots` are used in one component, each such `Child Slot` must have a unique key associated. The keys are required to be unique only within the parent (hosting) component, so it is ok for different components to have `Child Slots` with same keys. An exception will be thrown if multiple `Child Slots` with the same key are detected in a component.
+
+```kotlin title="Two Child Slots in one component"
+class Root(
+    componentContext: ComponentContext
+) : ComponentContext by componentContext {
+
+    private val topNavigation = SlotNavigation<TopConfig>()
+    
+    private val topSlot =
+        childSlot<TopConfig, TopChild>(
+            source = topNavigation,
+            key = "TopSlot",
+            // Omitted code
+        )
+
+    private val bottomNavigation = SlotNavigation<BottomConfig>()
+    
+    private val bottomSlot =
+        childSlot<BottomConfig, BottomChild>(
+            source = bottomNavigation,
+            key = "BottomSlot",
+            // Omitted code
+        )
+}
+```
diff --git a/docs/navigation/stack/component-context.md b/docs/navigation/stack/component-context.md
index d8ecba72..b5f113a6 100644
--- a/docs/navigation/stack/component-context.md
+++ b/docs/navigation/stack/component-context.md
@@ -2,47 +2,30 @@
 
 Custom `ComponentContext` allows passing extra data and functionality to every child component. See [Custom ComponentContext](../../component/custom-component-context.md) page for more information about creating custom `AppComponentContext`.
 
-In order to pass custom component context like `AppComponentContext` to child stack components, make an extension function on your `AppComponentContext` interface. This custom extension function will initialize the `Child Stack` and provide every child an `AppComponentContext`.
+In order to pass custom component context (like `AppComponentContext`) to child stack components, make an extension function on your `AppComponentContext` interface. This custom extension function will initialize the `Child Stack` and provide every child an `AppComponentContext`.
 
 ```kotlin
-fun <C : Parcelable, T : Any> AppComponentContext.appChildStack(
+inline fun <reified C : Parcelable, T : Any> AppComponentContext.appChildStack(
     source: StackNavigationSource<C>,
-    initialStack: () -> List<C>,
-    configurationClass: KClass<out C>,
+    noinline initialStack: () -> List<C>,
     key: String = "DefaultStack",
     handleBackButton: Boolean = false,
-    childFactory: (configuration: C, AppComponentContext) -> T
+    noinline childFactory: (configuration: C, AppComponentContext) -> T
 ): Value<ChildStack<C, T>> =
     childStack(
         source = source,
         initialStack = initialStack,
-        configurationClass = configurationClass,
         key = key,
         handleBackButton = handleBackButton
     ) { configuration, componentContext ->
         childFactory(
             configuration,
             DefaultAppComponentContext(
-                componentContext = componentContext
+                componentContext = componentContext,
+                // Additional dependencies here
             )
         )
     }
-
-inline fun <reified C : Parcelable, T : Any> AppComponentContext.appChildStack(
-    source: StackNavigationSource<C>,
-    noinline initialStack: () -> List<C>,
-    key: String = "DefaultStack",
-    handleBackButton: Boolean = false,
-    noinline childFactory: (configuration: C, AppComponentContext) -> T
-): Value<ChildStack<C, T>> =
-    appChildStack(
-        source = source,
-        initialStack = initialStack,
-        configurationClass = C::class,
-        key = key,
-        handleBackButton = handleBackButton,
-        childFactory = childFactory,
-    )
 ```
 
 Finally, in your components you can use the new extension function that will utilize the custom `AppComponentContext`.
@@ -65,7 +48,7 @@ class DefaultRootComponent(
     private val navigation = StackNavigation<Config>()
 
     private val _childStack =
-        childStack(
+        appChildStack(
             source = navigation,
             initialConfiguration = Config.List,
             handleBackButton = true, // Pop the back stack on back button press
diff --git a/mkdocs.yml b/mkdocs.yml
index 916223d4..5e5a39c4 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -39,10 +39,10 @@ nav:
       - Deep linking: navigation/stack/deeplinking.md
       - Web browser history: navigation/stack/browser-history.md
       - Custom ComponentContext: navigation/stack/component-context.md
-    - Child Overlay:
-      - Overview: navigation/overlay/overview.md
-      - Navigation: navigation/overlay/navigation.md
-      - Custom ComponentContext: navigation/overlay/component-context.md
+    - Child Slot:
+      - Overview: navigation/slot/overview.md
+      - Navigation: navigation/slot/navigation.md
+      - Custom ComponentContext: navigation/slot/component-context.md
     - Generic Navigation:
         - Overview: navigation/children/overview.md
 
diff --git a/sample/app-ios/app-ios.xcodeproj/project.pbxproj b/sample/app-ios/app-ios.xcodeproj/project.pbxproj
index f92d1c79..b0e32751 100644
--- a/sample/app-ios/app-ios.xcodeproj/project.pbxproj
+++ b/sample/app-ios/app-ios.xcodeproj/project.pbxproj
@@ -8,7 +8,6 @@
 
 /* Begin PBXBuildFile section */
 		798DB83B282F05EE0072CCBD /* app_iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798DB83A282F05EE0072CCBD /* app_iosApp.swift */; };
-		798DB83D282F05EE0072CCBD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798DB83C282F05EE0072CCBD /* ContentView.swift */; };
 		798DB83F282F05F20072CCBD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 798DB83E282F05F20072CCBD /* Assets.xcassets */; };
 		798DB842282F05F20072CCBD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 798DB841282F05F20072CCBD /* Preview Assets.xcassets */; };
 		798DB84A282F08670072CCBD /* CounterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798DB849282F08670072CCBD /* CounterView.swift */; };
@@ -20,15 +19,14 @@
 		798DB856282F0E6F0072CCBD /* MultiPaneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798DB855282F0E6F0072CCBD /* MultiPaneView.swift */; };
 		798DB858282F0EDF0072CCBD /* ArticleListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798DB857282F0EDF0072CCBD /* ArticleListView.swift */; };
 		798DB85A282F11800072CCBD /* ArticleDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798DB859282F11800072CCBD /* ArticleDetailsView.swift */; };
-		B308A81878CCE92C321B927F /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B308A300355A5948E5DC6CB8 /* StackView.swift */; };
 		B308A1B4FFA434881C6DF1E4 /* KittenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B308ADB617C2FED044531853 /* KittenView.swift */; };
 		B308A68175BF2D307474DCCC /* CustomNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B308A848D511CF1748757FD1 /* CustomNavigationView.swift */; };
+		B308A81878CCE92C321B927F /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B308A300355A5948E5DC6CB8 /* StackView.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
 		798DB837282F05EE0072CCBD /* app-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "app-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		798DB83A282F05EE0072CCBD /* app_iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = app_iosApp.swift; sourceTree = "<group>"; };
-		798DB83C282F05EE0072CCBD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 		798DB83E282F05F20072CCBD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		798DB841282F05F20072CCBD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
 		798DB849282F08670072CCBD /* CounterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterView.swift; sourceTree = "<group>"; };
@@ -76,7 +74,6 @@
 			isa = PBXGroup;
 			children = (
 				798DB83A282F05EE0072CCBD /* app_iosApp.swift */,
-				798DB83C282F05EE0072CCBD /* ContentView.swift */,
 				798DB83E282F05F20072CCBD /* Assets.xcassets */,
 				798DB840282F05F20072CCBD /* Preview Content */,
 				798DB849282F08670072CCBD /* CounterView.swift */,
@@ -195,7 +192,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				798DB854282F09FE0072CCBD /* RootView.swift in Sources */,
-				798DB83D282F05EE0072CCBD /* ContentView.swift in Sources */,
 				798DB858282F0EDF0072CCBD /* ArticleListView.swift in Sources */,
 				798DB83B282F05EE0072CCBD /* app_iosApp.swift in Sources */,
 				798DB84E282F08E00072CCBD /* MutableValue.swift in Sources */,
diff --git a/sample/app-ios/app-ios/ContentView.swift b/sample/app-ios/app-ios/ContentView.swift
deleted file mode 100644
index a9870759..00000000
--- a/sample/app-ios/app-ios/ContentView.swift
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-//  ContentView.swift
-//  app-ios
-//
-//  Created by Arkadii Ivanov on 13/05/2022.
-//
-
-import SwiftUI
-import Shared
-
-struct ContentView: View {
-    var body: some View {
-        Text("Hello, world!")
-            .padding()
-    }
-}
-
-struct ContentView_Previews: PreviewProvider {
-    static var previews: some View {
-        ContentView()
-    }
-}
diff --git a/sample/app-ios/app-ios/CounterView.swift b/sample/app-ios/app-ios/CounterView.swift
index 9e61fdbc..8be754b4 100644
--- a/sample/app-ios/app-ios/CounterView.swift
+++ b/sample/app-ios/app-ios/CounterView.swift
@@ -15,14 +15,14 @@ struct CounterView: View {
     private var observableModel: ObservableValue<CounterComponentModel>
     
     @ObservedObject
-    private var dialogOverlay: ObservableValue<ChildOverlay<AnyObject, DialogComponent>>
+    private var dialogSlot: ObservableValue<ChildSlot<AnyObject, DialogComponent>>
     
     private var model: CounterComponentModel { observableModel.value }
     
     init(_ counter: CounterComponent) {
         self.counter = counter
         observableModel = ObservableValue(counter.model)
-        dialogOverlay = ObservableValue(counter.dialogOverlay)
+        dialogSlot = ObservableValue(counter.dialogSlot)
     }
     
     var body: some View {
@@ -38,7 +38,7 @@ struct CounterView: View {
         }
         .navigationBarTitle(model.title, displayMode: .inline)
         .alert(
-            item: dialogOverlay.value.overlay?.instance,
+            item: dialogSlot.value.child?.instance,
             onDismiss: { $0.onDismissClicked() },
             title: { Text($0.title) },
             message: { Text($0.message) },
@@ -83,8 +83,8 @@ class PreviewCounterComponent : CounterComponent {
         )
     )
     
-    let dialogOverlay: Value<ChildOverlay<AnyObject, DialogComponent>> =
-    mutableValue(ChildOverlay(overlay: nil))
+    let dialogSlot: Value<ChildSlot<AnyObject, DialogComponent>> =
+    mutableValue(ChildSlot(child: nil))
     
     func onInfoClicked() {}
     func onNextClicked() {}
diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt
index 0c44f9b3..bb59b359 100644
--- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt
+++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt
@@ -18,7 +18,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.unit.dp
 import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
-import com.arkivanov.decompose.router.overlay.ChildOverlay
+import com.arkivanov.decompose.router.slot.ChildSlot
 import com.arkivanov.decompose.value.MutableValue
 import com.arkivanov.decompose.value.Value
 import com.arkivanov.sample.shared.counters.counter.CounterComponent.Model
@@ -83,8 +83,8 @@ internal fun CounterContent(component: CounterComponent, modifier: Modifier = Mo
         Spacer(modifier = Modifier.weight(1F))
     }
 
-    val dialogOverlay by component.dialogOverlay.subscribeAsState()
-    dialogOverlay.overlay?.instance?.also {
+    val dialogSlot by component.dialogSlot.subscribeAsState()
+    dialogSlot.child?.instance?.also {
         DialogContent(dialogComponent = it)
     }
 }
@@ -105,8 +105,8 @@ internal class PreviewCounterComponent : CounterComponent {
             )
         )
 
-    override val dialogOverlay: Value<ChildOverlay<*, DialogComponent>> =
-        MutableValue(ChildOverlay<Unit, DialogComponent>())
+    override val dialogSlot: Value<ChildSlot<Unit, DialogComponent>> =
+        MutableValue(ChildSlot())
 
     override fun onNextClicked() {}
     override fun onPrevClicked() {}
diff --git a/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt b/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt
index f02bb2ff..31410148 100644
--- a/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt
+++ b/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt
@@ -8,7 +8,7 @@ import com.arkivanov.decompose.extensions.android.ViewContext
 import com.arkivanov.decompose.extensions.android.context
 import com.arkivanov.decompose.extensions.android.layoutInflater
 import com.arkivanov.decompose.value.observe
-import com.arkivanov.essenty.lifecycle.doOnDestroy
+import com.arkivanov.essenty.lifecycle.subscribe
 import com.arkivanov.sample.shared.R
 import com.arkivanov.sample.shared.dialog.DialogComponent
 import com.google.android.material.appbar.MaterialToolbar
@@ -42,8 +42,8 @@ internal fun ViewContext.CounterView(component: CounterComponent): View {
     }
 
     var dialog: AlertDialog? = null
-    component.dialogOverlay.observe(lifecycle) { model ->
-        val dialogComponent: DialogComponent? = model.overlay?.instance
+    component.dialogSlot.observe(lifecycle) { model ->
+        val dialogComponent: DialogComponent? = model.child?.instance
         if ((dialogComponent != null) && (dialog == null)) {
             dialog = showDialog(component = dialogComponent)
         } else if ((dialogComponent == null) && (dialog != null)) {
@@ -60,6 +60,12 @@ private fun ViewContext.showDialog(component: DialogComponent): AlertDialog =
     AlertDialog.Builder(context)
         .setTitle(component.title)
         .setMessage(component.message)
-        .setNegativeButton("OK") { _, _ -> component.onDismissClicked() }
+        .setNegativeButton(android.R.string.ok, null)
         .show()
-        .also { dialog -> lifecycle.doOnDestroy(dialog::dismiss) }
+        .also { dialog ->
+            val observer = lifecycle.subscribe(onDestroy = dialog::dismiss)
+            dialog.setOnDismissListener {
+                lifecycle.unsubscribe(observer)
+                component.onDismissClicked()
+            }
+        }
diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponent.kt
index 2a290be6..244e769d 100644
--- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponent.kt
+++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponent.kt
@@ -1,13 +1,13 @@
 package com.arkivanov.sample.shared.counters.counter
 
-import com.arkivanov.decompose.router.overlay.ChildOverlay
+import com.arkivanov.decompose.router.slot.ChildSlot
 import com.arkivanov.decompose.value.Value
 import com.arkivanov.sample.shared.dialog.DialogComponent
 
 interface CounterComponent {
 
     val model: Value<Model>
-    val dialogOverlay: Value<ChildOverlay<*, DialogComponent>>
+    val dialogSlot: Value<ChildSlot<*, DialogComponent>>
 
     fun onInfoClicked()
 
diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt
index 4929a826..c0ded238 100644
--- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt
+++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt
@@ -1,11 +1,11 @@
 package com.arkivanov.sample.shared.counters.counter
 
 import com.arkivanov.decompose.ComponentContext
-import com.arkivanov.decompose.router.overlay.ChildOverlay
-import com.arkivanov.decompose.router.overlay.OverlayNavigation
-import com.arkivanov.decompose.router.overlay.activate
-import com.arkivanov.decompose.router.overlay.childOverlay
-import com.arkivanov.decompose.router.overlay.dismiss
+import com.arkivanov.decompose.router.slot.ChildSlot
+import com.arkivanov.decompose.router.slot.SlotNavigation
+import com.arkivanov.decompose.router.slot.activate
+import com.arkivanov.decompose.router.slot.childSlot
+import com.arkivanov.decompose.router.slot.dismiss
 import com.arkivanov.decompose.value.MutableValue
 import com.arkivanov.decompose.value.Value
 import com.arkivanov.decompose.value.operator.map
@@ -42,10 +42,10 @@ internal class DefaultCounterComponent(
 
     override val model: Value<Model> = handler.state.map { it.toModel() }
 
-    private val dialogNavigation = OverlayNavigation<DialogConfig>()
+    private val dialogNavigation = SlotNavigation<DialogConfig>()
 
-    private val _dialogOverlay =
-        childOverlay<DialogConfig, DialogComponent>(
+    private val _dialogSlot =
+        childSlot<DialogConfig, DialogComponent>(
             source = dialogNavigation,
             persistent = false,
             handleBackButton = true,
@@ -58,7 +58,7 @@ internal class DefaultCounterComponent(
             }
         )
 
-    override val dialogOverlay: Value<ChildOverlay<*, DialogComponent>> = _dialogOverlay
+    override val dialogSlot: Value<ChildSlot<*, DialogComponent>> = _dialogSlot
 
     override fun onInfoClicked() {
         dialogNavigation.activate(DialogConfig(count = handler.state.value.count))
diff --git a/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt b/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt
index 22b5fce8..c825147e 100644
--- a/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt
+++ b/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt
@@ -91,8 +91,8 @@ internal val CounterContent: FC<RProps<CounterComponent>> = FC { props ->
         }
     }
 
-    val dialogOverlay by props.component.dialogOverlay.useAsState()
-    dialogOverlay.overlay?.instance?.also { dialog ->
+    val dialogSlot by props.component.dialogSlot.useAsState()
+    dialogSlot.child?.instance?.also { dialog ->
         componentContent(component = dialog, content = DialogComponentContent)
     }
 }


Test patch:
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt
new file mode 100644
index 00000000..ab288b4e
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt
@@ -0,0 +1,316 @@
+package com.arkivanov.decompose.router.slot
+
+import com.arkivanov.decompose.Child
+import com.arkivanov.decompose.ComponentContext
+import com.arkivanov.decompose.DefaultComponentContext
+import com.arkivanov.decompose.router.TestInstance
+import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
+import com.arkivanov.decompose.value.Value
+import com.arkivanov.decompose.value.getValue
+import com.arkivanov.essenty.backhandler.BackDispatcher
+import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
+import com.arkivanov.essenty.instancekeeper.getOrCreate
+import com.arkivanov.essenty.lifecycle.Lifecycle
+import com.arkivanov.essenty.lifecycle.LifecycleRegistry
+import com.arkivanov.essenty.lifecycle.resume
+import com.arkivanov.essenty.parcelable.Parcelable
+import com.arkivanov.essenty.parcelable.Parcelize
+import com.arkivanov.essenty.statekeeper.consume
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
+import kotlin.test.assertNotSame
+import kotlin.test.assertSame
+import kotlin.test.assertTrue
+
+@Suppress("TestFunctionName")
+class ChildSlotIntegrationTest {
+
+    private val navigation = SlotNavigation<Config>()
+    private val lifecycle = LifecycleRegistry()
+    private val stateKeeperDispatcher = TestStateKeeperDispatcher()
+    private val instanceKeeperDispatcher = InstanceKeeperDispatcher()
+    private val backDispatcher = BackDispatcher()
+
+    private val context =
+        DefaultComponentContext(
+            lifecycle = lifecycle,
+            stateKeeper = stateKeeperDispatcher,
+            instanceKeeper = instanceKeeperDispatcher,
+            backHandler = backDispatcher,
+        )
+
+    @BeforeTest
+    fun before() {
+        lifecycle.resume()
+    }
+
+    @Test
+    fun WHEN_created_without_configuration_THEN_child_not_active() {
+        val slot by context.childSlot(initialConfiguration = null)
+
+        slot.assertChild(null)
+    }
+
+    @Test
+    fun WHEN_created_with_configuration_THEN_slot_active() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+
+        slot.assertChild(1)
+    }
+
+    @Test
+    fun GIVEN_not_active_WHEN_activate_THEN_slot_active() {
+        val slot by context.childSlot(initialConfiguration = null)
+
+        navigation.activate(Config(1))
+
+        slot.assertChild(1)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_dismiss_THEN_slot_not_active() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+
+        navigation.dismiss()
+
+        slot.assertChild(null)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_slot_active() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+
+        navigation.activate(Config(1))
+
+        slot.assertChild(1)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_instance_active() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+        val instance = slot.requireChild().instance
+
+        navigation.activate(Config(1))
+
+        assertSame(instance, slot.child?.instance)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_activate_with_other_configuration_THEN_other_slot_active() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+
+        navigation.activate(Config(2))
+
+        slot.assertChild(2)
+    }
+
+    @Test
+    fun GIVEN_not_active_WHEN_activate_THEN_lifecycle_resumed() {
+        val slot by context.childSlot(initialConfiguration = null)
+
+        navigation.activate(Config(1))
+
+        assertEquals(Lifecycle.State.RESUMED, slot.requireChild().instance.lifecycle.state)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_dismiss_THEN_lifecycle_destroyed() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+        val lifecycle = slot.requireChild().instance.lifecycle
+
+        navigation.dismiss()
+
+        assertEquals(Lifecycle.State.DESTROYED, lifecycle.state)
+    }
+
+    @Test
+    fun GIVEN_not_active_WHEN_parent_backDispatcher_isEnabled_THEN_false() {
+        context.childSlot(initialConfiguration = null)
+
+        assertFalse(backDispatcher.isEnabled)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_parent_backDispatcher_isEnabled_THEN_true() {
+        context.childSlot(initialConfiguration = Config(1))
+
+        assertTrue(backDispatcher.isEnabled)
+    }
+
+    @Test
+    fun GIVEN_active_WHEN_back_pressed_THEN_slot_not_active() {
+        val slot by context.childSlot(initialConfiguration = Config(1))
+
+        backDispatcher.back()
+
+        slot.assertChild(null)
+    }
+
+    @Test
+    fun GIVEN_dismissed_via_back_pressed_WHEN_parent_backDispatcher_isEnabled_THEN_false() {
+        context.childSlot(initialConfiguration = Config(1))
+        backDispatcher.back()
+
+        assertFalse(backDispatcher.isEnabled)
+    }
+
+    @Test
+    fun GIVEN_persistent_WHEN_recreated_THEN_slot_active() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        oldContext.childSlot(initialConfiguration = Config(1), persistent = true)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newSlot by newContext.childSlot(initialConfiguration = null, persistent = true)
+
+        newSlot.assertChild(1)
+    }
+
+    @Test
+    fun GIVEN_persistent_WHEN_recreated_THEN_slot_state_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val slot by oldContext.childSlot(initialConfiguration = Config(1), persistent = true)
+        slot.requireChild().instance.stateKeeper.register("key") { Config(10) }
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newSlot by newContext.childSlot(initialConfiguration = null, persistent = true)
+        val restoredState = newSlot.requireChild().instance.stateKeeper.consume<Config>("key")
+
+        assertEquals(Config(10), restoredState)
+    }
+
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_slot_not_active() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        oldContext.childSlot(initialConfiguration = Config(1), persistent = false)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newSlot by newContext.childSlot(initialConfiguration = null, persistent = false)
+
+        newSlot.assertChild(null)
+    }
+
+    @Test
+    fun WHEN_created_persistent_THEN_registered_in_parent_StateKeeper() {
+        context.childSlot(initialConfiguration = Config(1), persistent = true)
+
+        stateKeeperDispatcher.assertSupplierRegistered(key = "key")
+    }
+
+    @Test
+    fun GIVEN_persistent_WHEN_recreated_THEN_slot_instance_retained() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = true)
+        val oldInstance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        val newSlot by newContext.childSlot(initialConfiguration = null, persistent = true)
+        val retainedInstance = newSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        assertSame(oldInstance, retainedInstance)
+    }
+
+    @Test
+    fun GIVEN_persistent_WHEN_recreated_THEN_slot_instance_not_destroyed() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = true)
+        val instance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.childSlot(initialConfiguration = null, persistent = true)
+
+        assertFalse(instance.isDestroyed)
+    }
+
+    @Test
+    fun WHEN_created_persistent_THEN_registered_in_parent_InstanceKeeper() {
+        context.childSlot(initialConfiguration = Config(1), persistent = true)
+
+        assertNotNull(instanceKeeperDispatcher.get(key = "key"))
+    }
+
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_slot_instance_not_retained() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = false)
+        val oldInstance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        val newSlot by newContext.childSlot(initialConfiguration = Config(1), persistent = false)
+        val newInstance = newSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        assertNotSame(oldInstance, newInstance)
+    }
+
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_slot_instance_destroyed() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = false)
+        val instance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.childSlot(initialConfiguration = null, persistent = false)
+
+        assertTrue(instance.isDestroyed)
+    }
+
+    private fun ComponentContext.childSlot(
+        initialConfiguration: Config?,
+        persistent: Boolean = true,
+    ): Value<ChildSlot<Config, Component>> =
+        childSlot(
+            source = navigation,
+            key = "key",
+            initialConfiguration = { initialConfiguration },
+            handleBackButton = true,
+            persistent = persistent,
+            childFactory = ::Component,
+        )
+
+    private fun ChildSlot<Config, Component>.requireChild(): Child.Created<Config, Component> =
+        requireNotNull(child)
+
+    private fun ChildSlot<Config, Component>.assertChild(id: Int?) {
+        assertEquals(id, child?.configuration?.id)
+        assertEquals(id, child?.instance?.id)
+    }
+
+    @Parcelize
+    private data class Config(
+        val id: Int,
+    ) : Parcelable
+
+    private class Component(
+        config: Config,
+        componentContext: ComponentContext,
+    ) : ComponentContext by componentContext {
+        val id: Int = config.id
+    }
+}
diff --git a/sample/shared/compose/src/jvmTest/kotlin/com/arkivanov/sample/shared/counters/counter/TestCounterComponent.kt b/sample/shared/compose/src/jvmTest/kotlin/com/arkivanov/sample/shared/counters/counter/TestCounterComponent.kt
index b5209182..9e58518b 100644
--- a/sample/shared/compose/src/jvmTest/kotlin/com/arkivanov/sample/shared/counters/counter/TestCounterComponent.kt
+++ b/sample/shared/compose/src/jvmTest/kotlin/com/arkivanov/sample/shared/counters/counter/TestCounterComponent.kt
@@ -1,6 +1,6 @@
 package com.arkivanov.sample.shared.counters.counter
 
-import com.arkivanov.decompose.router.overlay.ChildOverlay
+import com.arkivanov.decompose.router.slot.ChildSlot
 import com.arkivanov.decompose.value.MutableValue
 import com.arkivanov.sample.shared.counters.counter.CounterComponent.Model
 import com.arkivanov.sample.shared.dialog.DialogComponent
@@ -8,7 +8,7 @@ import com.arkivanov.sample.shared.dialog.DialogComponent
 class TestCounterComponent(model: Model = Model()) : CounterComponent {
 
     override val model: MutableValue<Model> = MutableValue(model)
-    override val dialogOverlay: MutableValue<ChildOverlay<Any, DialogComponent>> = MutableValue(ChildOverlay())
+    override val dialogSlot: MutableValue<ChildSlot<Any, DialogComponent>> = MutableValue(ChildSlot())
 
     var isNextClicked: Boolean = false
     var isPrevClicked: Boolean = false
diff --git a/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponentTest.kt b/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponentTest.kt
index f35f6621..bdda069e 100644
--- a/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponentTest.kt
+++ b/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/counters/counter/CounterComponentTest.kt
@@ -1,7 +1,7 @@
 package com.arkivanov.sample.shared.counters.counter
 
 import com.arkivanov.decompose.DefaultComponentContext
-import com.arkivanov.decompose.router.overlay.overlay
+import com.arkivanov.decompose.router.slot.child
 import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
 import com.arkivanov.essenty.lifecycle.LifecycleRegistry
@@ -109,8 +109,8 @@ class CounterComponentTest {
 
         component.onInfoClicked()
 
-        assertEquals("Counter", component.dialogOverlay.overlay?.instance?.title)
-        assertEquals("Value: 001", component.dialogOverlay.overlay?.instance?.message)
+        assertEquals("Counter", component.dialogSlot.child?.instance?.title)
+        assertEquals("Value: 001", component.dialogSlot.child?.instance?.message)
     }
 
     @Test
@@ -118,9 +118,9 @@ class CounterComponentTest {
         createComponent()
         component.onInfoClicked()
 
-        component.dialogOverlay.overlay?.instance?.onDismissClicked()
+        component.dialogSlot.child?.instance?.onDismissClicked()
 
-        assertNull(component.dialogOverlay.overlay)
+        assertNull(component.dialogSlot.child)
     }
 
     @Test