Skip to main content
Version: 4.2

Koin Compiler Plugin

The Koin Compiler Plugin is the recommended approach for all new Kotlin 2.x projects. It's a native Kotlin compiler plugin that powers both DSL and Annotations with auto-wiring, compile-time safety, and cleaner syntax.

What is the Compiler Plugin?

The Koin Compiler Plugin is a native Kotlin Compiler Plugin (K2) - not KSP or annotation processing. It integrates directly with the Kotlin compiler to:

  • Auto-detect constructor parameters - No manual get() calls needed
  • Transform code at compile-time - Errors caught during build
  • Work with both DSL and Annotations - Your choice of style
  • Generate no visible files - Cleaner project structure

Why Use the Compiler Plugin?

1. Safer Code

The plugin auto-detects constructor dependencies, reducing manual wiring errors:

// Without Compiler Plugin - easy to make mistakes
val appModule = module {
single { UserService(get(), get(), get()) } // Hope you got the order right!
}

// With Compiler Plugin - auto-wired
val appModule = module {
single<UserService>() // Plugin detects all constructor parameters
}

2. Cleaner Syntax

Less boilerplate, more readable:

Classic DSLCompiler Plugin DSL
singleOf(::MyService)single<MyService>()
single { MyService(get(), get()) }single<MyService>()
factoryOf(::MyRepo)factory<MyRepo>()
viewModelOf(::MyVM)viewModel<MyVM>()
scopedOf(::MyPresenter)scoped<MyPresenter>()
workerOf(::MyWorker)worker<MyWorker>()

3. Compile-Time Safety (Coming Soon)

The Koin Compiler Plugin will provide compile-time dependency verification for both DSL and Annotations:

  • Validate that all dependencies can be resolved
  • Detect missing definitions at compile time
  • Catch configuration errors before runtime
Work in Progress

Compile-time safety is currently in development. When released, it will replace the KSP-based KOIN_CONFIG_CHECK option with native compiler integration.

4. DSL & Annotations - Both Equally Powerful

Use whichever style you prefer - the same plugin powers both with identical capabilities:

DSL Style:

val appModule = module {
single<Database>()
single<UserRepository>()
viewModel<UserViewModel>()
}
DSL + Parameter Annotations

When using DSL style, you still use parameter annotations on your classes to guide the plugin:

class UserPresenter(
@InjectedParam val userId: String, // Runtime parameter
@Named("api") val client: ApiClient, // Qualified dependency
val repository: UserRepository // Auto-resolved
)

val appModule = module {
factory<UserPresenter>() // Plugin reads annotations from the class
}

The DSL defines where dependencies are registered. Parameter annotations define how they are resolved.

Annotation Style:

@Singleton
class Database

@Singleton
class UserRepository(private val database: Database)

@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()

Getting Started

Setup

Add the Compiler Plugin to your project.

info

See the Compiler Plugin Setup Guide for detailed instructions.

Using the Compiler Plugin DSL

Import from the compiler plugin package:

import org.koin.plugin.module.dsl.*
import org.koin.dsl.module

val appModule = module {
single<Database>()
single<ApiClient>()
single<UserRepository>()
viewModel<UserViewModel>()
}
note

The Compiler Plugin DSL is in org.koin.plugin.module.dsl. Classic DSL remains in org.koin.dsl.

Using Annotations

Annotations work the same as before:

@Singleton
class Database

@Singleton
class ApiClient

@Singleton
class UserRepository(
private val database: Database,
private val apiClient: ApiClient
)

@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()

@Module
@ComponentScan("com.myapp")
class AppModule

How It Works

The Compiler Plugin operates in two phases:

1. FIR Phase (Analysis)

During the Frontend Intermediate Representation phase, the plugin:

  • Analyzes your module definitions
  • Detects constructor parameters
  • Validates dependency declarations

2. IR Phase (Transformation)

During the Intermediate Representation phase, the plugin:

  • Generates proper get() calls for each parameter
  • Handles qualifiers (@Named)
  • Handles injected parameters (@InjectedParam)
  • Handles nullable and Lazy types

What Gets Generated

When you write:

single<UserRepository>()

The plugin transforms it to:

single { UserRepository(get(), get()) }  // Parameters auto-detected

For more complex cases:

// Your code
@Singleton
class MyService(
val required: RequiredDep,
val optional: OptionalDep?,
@Named("special") val named: NamedDep,
val lazy: Lazy<LazyDep>,
@InjectedParam val param: String
)

The plugin generates proper handling for each parameter type:

  • Required: get()
  • Optional: getOrNull()
  • Named: get(named("special"))
  • Lazy: inject()
  • InjectedParam: params.get()

Compiler Plugin DSL Reference

Definition Types

import org.koin.plugin.module.dsl.*

val appModule = module {
// Singleton - one instance
single<MyService>()

// Factory - new instance each time
factory<MyPresenter>()

// Scoped - instance per scope
scope<MyActivity> {
scoped<ActivityPresenter>()
}

// ViewModel
viewModel<MyViewModel>()

// Worker (Android WorkManager)
worker<MyWorker>()
}

With Qualifiers

Use @Named on your classes to define qualifiers, and on parameters to specify which dependency to inject:

// Define implementations with @Named qualifier
@Named("local")
class LocalDatabase : Database

@Named("remote")
class RemoteDatabase : Database

// Use @Named on parameters to specify which one to inject
class SyncService(
@Named("local") val localDb: Database,
@Named("remote") val remoteDb: Database
)

// DSL - plugin reads @Named from classes and parameters
val appModule = module {
single<LocalDatabase>()
single<RemoteDatabase>()
single<SyncService>()
}

You can also create custom qualifiers with @Qualifier:

@Qualifier
annotation class LocalDb

@Qualifier
annotation class RemoteDb

@LocalDb
class LocalDatabase : Database

@RemoteDb
class RemoteDatabase : Database

class SyncService(
@LocalDb val localDb: Database,
@RemoteDb val remoteDb: Database
)

With Parameters

Use @InjectedParam on your class to mark parameters passed at injection time:

// Annotation on the class - tells the plugin how to handle this parameter
class UserPresenter(
@InjectedParam val userId: String, // Passed via parametersOf()
val repository: UserRepository // Auto-resolved by Koin
)

// DSL in module - tells Koin where to register
val appModule = module {
factory<UserPresenter>()
}

// Usage - pass the runtime parameter
val presenter: UserPresenter = get { parametersOf("user123") }

Interface Binding

val appModule = module {
single<UserRepositoryImpl>() bind UserRepository::class

// Or multiple bindings
single<MyServiceImpl>() binds arrayOf(
ServiceA::class,
ServiceB::class
)
}

Annotations Reference

Definition Annotations

AnnotationDescription
@Singleton / @SingleSingle instance
@FactoryNew instance each time
@ScopedInstance per scope
@KoinViewModelAndroid ViewModel
@KoinWorkerAndroid WorkManager Worker

Parameter Annotations

AnnotationDescription
@Named("qualifier")Named qualifier
@InjectedParamRuntime parameter (via parametersOf())
@Property("key")Koin property value
@ProvidedExternal dependency (skip validation)

Module Annotations

AnnotationDescription
@ModuleDeclares a Koin module
@ComponentScan("package")Scan package for annotated classes
@ConfigurationAuto-discovered module

Comparison: Approaches

ApproachStatusPackageSyntax
Compiler Plugin DSLRecommendedAlready located in Koin org.koin.plugin.module.dslsingle<MyService>(), factory<MyRepo>(), viewModel<MyVM>()
Compiler Plugin AnnotationsRecommendedAnnotations available in koin-annotations@Singleton, @Factory, @KoinViewModel
Classic DSLFully Supportedorg.koin.dslsingleOf(::MyService), single { MyService(get()) }, viewModelOf(::MyVM)
KSP AnnotationsDeprecatedkoin-ksp-compilerSame annotations, Migrate to Compiler Plugin ⚠️
  • Auto-detects dependencies
  • Compile-time analysis
  • Cleanest syntax
  • Auto-detects dependencies
  • Compile-time analysis
  • Familiar annotation style

Classic DSL (Fully Supported)

  • Works with any Kotlin version
  • Full control over wiring
  • Can migrate to Plugin DSL when ready

KSP Annotations (Deprecated)

  • Migrate to Compiler Plugin
  • Will be removed in future version

Migration

From Classic DSL

If you're using classic DSL, migration is optional but recommended:

  1. Add the Compiler Plugin to Gradle
  2. Update imports to org.koin.plugin.module.dsl.*
  3. Replace singleOf(::Class) with single<Class>()
  4. Remove manual get() calls

See Migrating from DSL to Compiler Plugin.

From KSP Annotations

If you're using KSP, migration is recommended now:

  1. Update Kotlin to 2.x
  2. Replace koin-ksp-compiler with Compiler Plugin
  3. Your annotations stay the same - no code changes!
  4. Delete generated files

See Migrating from KSP to Compiler Plugin.

Requirements

  • Kotlin 2.x (K2 compiler)
  • Gradle 8.x+

Configuration Options

// build.gradle.kts
koinCompiler {
// Options will be documented here
}

Classic DSL: Still Fully Supported

The Compiler Plugin doesn't replace Classic DSL - it adds analysis and generation on top. Classic DSL remains fully supported:

// Still works perfectly
val appModule = module {
singleOf(::Database)
singleOf(::ApiClient)
single { CustomService(get(), get(), configValue) } // Custom logic
viewModelOf(::UserViewModel)
}

Use Classic DSL when you need:

  • Custom factory logic
  • getOrNull() for optional dependencies
  • Conditional instantiation
  • Backward compatibility with Kotlin 1.x

Next Steps