docs/reference/koin-compose/compose-modules.md
Koin provides APIs to dynamically load and unload modules tied to Composable lifecycle. This is useful for feature modules, lazy loading, and on-demand dependencies.
Load Koin modules when a Composable enters composition:
val featureModule = module {
factory<FeatureRepository>()
viewModel<FeatureViewModel>()
}
@Composable
fun FeatureScreen() {
// Load module when this Composable enters composition
rememberKoinModules(featureModule)
val viewModel = koinViewModel<FeatureViewModel>()
}
@Composable
fun FeatureScreen() {
rememberKoinModules(
featureDataModule,
featureDomainModule,
featureUiModule
)
}
Control when modules are unloaded:
@Composable
fun FeatureScreen() {
rememberKoinModules(
featureModule,
unloadOnForgotten = true, // Unload when Composable leaves
unloadOnAbandoned = true // Unload if composition fails
)
}
| Option | When Triggered |
|---|---|
unloadOnForgotten | Composable removed from composition |
unloadOnAbandoned | Composition fails or is abandoned |
Load feature-specific dependencies on demand:
// Feature module in separate Gradle module
val checkoutModule = module {
factory<PaymentProcessor>()
factory<CheckoutRepository>()
viewModel<CheckoutViewModel>()
}
@Composable
fun CheckoutScreen() {
rememberKoinModules(checkoutModule, unloadOnForgotten = true)
val viewModel = koinViewModel<CheckoutViewModel>()
CheckoutContent(viewModel)
}
Combine with navigation for lazy feature loading:
NavHost(navController, startDestination = "home") {
composable("home") {
HomeScreen() // No extra modules needed
}
composable("checkout") {
// Checkout module loaded only when navigating here
rememberKoinModules(checkoutModule, unloadOnForgotten = true)
CheckoutScreen()
}
composable("profile") {
// Profile module loaded only when navigating here
rememberKoinModules(profileModule, unloadOnForgotten = true)
ProfileScreen()
}
}
Swap implementations for previews:
val debugModule = module {
single<ApiClient> { MockApiClient() }
}
@Preview
@Composable
fun FeatureScreenPreview() {
rememberKoinModules(debugModule)
FeatureScreen()
}
Load modules based on conditions:
@Composable
fun App(isDebug: Boolean) {
if (isDebug) {
rememberKoinModules(debugModule)
}
MainScreen()
}
Combine with Koin's lazy module loading for better performance:
val featureModule = lazyModule {
// Definitions parsed lazily when module is loaded
factory<HeavyService>()
viewModel<FeatureViewModel>()
}
@Composable
fun FeatureScreen() {
rememberKoinModules(featureModule, unloadOnForgotten = true)
val viewModel = koinViewModel<FeatureViewModel>()
}
Use unloadOnForgotten = true - prevents memory leaks
rememberKoinModules(featureModule, unloadOnForgotten = true)
One module per feature - keep modules focused and independent
Combine with lazy modules - for large apps with many features
val featureModule = lazyModule { /* ... */ }
Load at navigation level - load modules in NavHost composables
Avoid circular dependencies - feature modules should not depend on each other