Skip to main content
Version: 4.2

What is Koin?

Koin is a dependency injection framework for Kotlin. This page explains how it works, what it costs and what it gives you, and how the project is organized. If dependency injection is new to you, read What is Dependency Injection? first.

How Koin works​

You describe your classes in modules, start Koin once, and ask for instances where you need them:

class ApiService
class UserRepository(private val api: ApiService)

val appModule = module {
single<ApiService>()
single<UserRepository>()
}

fun main() {
startKoin {
modules(appModule)
}
}

Two parts work together:

  • Runtime container: startKoin loads your modules into a container. Each definition is a Kotlin function that builds one type. When something asks for a UserRepository, Koin runs that function, resolves its constructor parameters from the container, and keeps or discards the instance depending on the definition type (single, factory, scoped). Koin does not build objects by reflection.
  • Compile-time checks (optional): the Koin Compiler Plugin is a Kotlin compiler plugin. It reads your constructors to generate the wiring, then checks at build time that every dependency of the assembled graph is declared. A missing definition becomes a build error instead of a crash.

Core Concepts explains modules, definitions, scopes and injection with one running example.

What the runtime model costs and gives​

Because the graph lives in a runtime container, Koin can change it while the app runs:

  • Dynamic modules: load and unload modules after start with loadKoinModules() and unloadKoinModules(), for feature flags or on-demand features. See Starting Koin.
  • Lazy modules: load part of the graph in the background to reduce startup work. See Lazy Modules.
  • Scopes created at runtime: open and close scopes for a session, a screen or a request. See Scopes.
  • No generated sources: nothing to commit, nothing to configure per Kotlin Multiplatform target.

The same model has costs you should know:

  • Startup work: modules are registered when Koin starts, and every lookup goes through the container. For most apps this is small, but eager definitions (createdAtStart) run on the thread that starts Koin.
  • Runtime errors without the plugin: with the Constructor or Classic DSL and no plugin, a missing definition is only found when the code runs. You can catch it in a unit test with verify(), or at build time with the Compiler Plugin.
  • Limits of compile-time checks: the plugin validates the graph assembled at a Koin entry point such as startKoin. Modules loaded later with loadKoinModules() can't be proven at build time, and a library module with no entry point is only checked when an app assembles it. See Compile-Time Safety.
  • Kotlin version coupling: the Compiler Plugin depends on compiler internals, so each plugin release is verified on a list of Kotlin versions. See Compiler Plugin Setup.

Constructor injection and service locator​

Your classes receive their dependencies through the constructor. Koin only retrieves an instance where it cannot create the object itself, at an entry point:

// Koin creates these: dependencies come through the constructor
class UserRepository(private val api: UserApi)
class UserViewModel(private val repository: UserRepository) : ViewModel()

// Android creates the Activity: it retrieves its ViewModel from Koin
class UserActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModel()
}

The dependency is visible in the signature, and you can test UserRepository and UserViewModel without Koin. Retrieval functions such as by inject(), by viewModel() or koinInject() belong to entry points: an Activity, a Fragment, a root Composable, main(), a Ktor route, a test. What is Dependency Injection? compares the two patterns, and Retrieving Dependencies covers get() and inject().

Two styles, one container​

StyleExampleNeeds
Compiler Plugin DSLsingle<UserRepository>()Koin Compiler Plugin
Annotations@Singleton class UserRepository(...)Koin Compiler Plugin, koin-annotations
Constructor DSLsingleOf(::UserRepository)Nothing extra (the plugin adds compile-time checks)
Classic DSLsingle { UserRepository(get()) }Nothing extra (the plugin adds compile-time checks)

Annotations are one style; the Compiler Plugin, Constructor and Classic DSL are the three forms of the other, the Koin DSL. All produce the same definitions in the same container. Pick one style per project. Choosing your style compares them and gives a default.

Annotations status​

The koin-annotations library (@Singleton, @Factory, @KoinViewModel, @Module, @ComponentScan) is part of the main Koin project: same repository, same version, same BOM. It is not deprecated. What is deprecated is koin-ksp-compiler, the KSP processor that used to read these annotations. The Koin Compiler Plugin replaces it, with the same annotations. See Migrating from KSP to the Compiler Plugin.

Platforms​

Koin is a Kotlin Multiplatform library. The core runs on every Kotlin target, and integration libraries add platform APIs:

PlatformMain artifactStart here
Kotlin/JVMkoin-coreKotlin tutorial
Androidkoin-androidStart Koin on Android
Jetpack Compose, Compose Multiplatformkoin-compose, koin-compose-viewmodelKoin for Compose
Kotlin Multiplatform (iOS, desktop, web, native)koin-coreKotlin Multiplatform Setup
Ktor serverkoin-ktorKoin for Ktor

The full artifact list is in Gradle Setup.

The Koin project​

Koin is open source under the Apache 2.0 license. It is made of:

If you know Hilt or Dagger, Koin vs Hilt compares the two approaches.