Koin for Kotlin Multiplatform and Compose Multiplatform: Quick Start Guide
Quick start guide on how to add Koin dependency injection for KMP projects, specifically for those using Compose Multiplatform for the view layer.

This article will guide you through adding Koin dependency injection for Kotlin Multiplatform (KMP) projects, specifically for those using Compose Multiplatform (CMP) for the view layer.
Koin Dependencies
Adding Koin dependencies to your KMP and CMP project is quite easy as nowadays we have a BOM available. You can check latest version on Maven Central Repository.
Define versions and dependencies in your .toml
file
At the moment of writing this article, the latest stable version of the Koin BOM is 4.1.0
, but make sure to double check on the maven central repository page.
# your project > gradle > libs.versions.toml
[versions]
koin-bom = "4.1.0"
[libraries]
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
koin-core = { module = "io.insert-koin:koin-core" }
koin-compose = { module = "io.insert-koin:koin-compose" }
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" }
Add dependencies to your build.gradle
file
For KMP projects using purely Compose Multiplatform for the UI layer, you only need to add these dependencies into your common source set:
// your project > composeApp > build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel)
}
}
}
Define a dependency injection module
To initialise Koin you need to define a dependency injection module. Create a common dependency injection module by simply creating a kotlin file in your commonMain
app module and using the Koin DSL:
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
val commonModule = module {
// dependencies will go here
}
Your common dependencies will be added here and we will use this module in the next step for initialising Koin.
Koin Initialisation in KMP and CMP
Once again, initialisation is easier to do for Compose Multiplatform projects. Usually you would have to implement initialisation for each platform you target, but for CMP projects you only need to initialise in a single place.
Open the main App.kt
file for your KMP project and wrap your Compose app in the Koin initialisation:
import org.koin.compose.KoinApplication
@Composable
@Preview
fun App() {
KoinApplication(application = {
modules(commonModule)
}) {
AppTheme {
// your app composables, navigation host etc.
}
}
}
How to inject ViewModels using Koin
Assuming you have a composable screen such as this:
import androidx.compose.runtime.Composable
@Composable
internal fun YourComposableScreenRoot(
modifier: Modifier,
viewModel: YourComposableViewModel,
) {
...
}
And you've defined a viewModel
like so:
import androidx.lifecycle.ViewModel
class YourComposableViewModel : ViewModel() {
...
}
You can make the view model injectable by first defining how to inject it via the common module we've declared before:
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
val commonModule = module {
viewModel { YourComposableViewModel() }
// or constructor DSL
viewModelOf(::YourComposableViewModel)
}
And then inject an instance using the dedicated Koin function when calling your composable screen in your navigation host or wherever else you are using it:
import org.koin.compose.viewmodel.koinViewModel
YourComposableScreenRoot(
modifier = Modifier.fillMaxSize(),
viewModel = koinViewModel<YourComposableViewModel>(),
)
This will work for CMP view models along all targeted platforms.
How to inject ViewModels when using Jetpack Navigation and Koin
Assuming you have the view model setup above and you are using androidx.navigation
, you probably have a navigation destination defined like so:
import kotlinx.serialization.Serializable
@Serializable
object YourDestination()
And you probably have a NavHost
defined like so:
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
@Composable
fun NavigationHost(
// nav host arguments
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = FirstScreen,
) {
// composable destinations go here
}
To include a Koin view model injection into your composable<>
destination definition you can simply use the same koinViewModel<>()
call as before:
import androidx.navigation.compose.composable
import org.koin.compose.viewmodel.koinViewModel
composable<YourDestination> {
YourComposableScreenRoot(
modifier = Modifier.fillMaxSize(),
viewModel = koinViewModel<YourComposableViewModel>(), // Koin call here
)
}
How to inject ViewModels with arguments when using Jetpack Navigation and Koin
Assuming your view model takes arguments:
import androidx.lifecycle.ViewModel
class YourComposableViewModel(
private val firstArgument: String,
private val secondArgument: Int,
) : ViewModel() {
...
}
First you will have to alter your DI module definition for the view model:
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
val commonModule = module {
viewModel { (firstArg: String, secondArg: Int) ->
YourComposableViewModel(
firstArgument = firstArg,
secondArgument = secondArg,
)
}
// and other DI definitions
}
Then make sure your navigation destination accepts the same arguments:
import kotlinx.serialization.Serializable
@Serializable
data class YourDestination(
val firstArgument: String,
val secondArgument: Int
)
And finally consume the navigation arguments in your NavHost
composable definitions:
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
composable<YourDestination> { backStack ->
// first get typed navigation arguments
val args = backStack.toRoute<YourDestination>()
// then get a view model injection with parameters
val vm: YourComposableViewModel = koinViewModel(
parameters = { parametersOf(args.firstArgument, args.secondArgument) }
)
// finally call the screen root with the injected view model
YourComposableScreenRoot(
modifier = Modifier.fillMaxSize(),
viewModel = vm,
)
}
And of course, to navigate towards this screen with arguments simply call the navigate
function of the nav controller with your desired destination and arguments:
import androidx.navigation.NavController
navController.navigate(YourDestination("Some string", 42))