Skip to main content
Version: 4.2

Dynamic Modules in Compose

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.

rememberKoinModules

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>()
}

Multiple Modules

@Composable
fun FeatureScreen() {
rememberKoinModules(
featureDataModule,
featureDomainModule,
featureUiModule
)
}

Unloading Modules

Control when modules are unloaded:

@Composable
fun FeatureScreen() {
rememberKoinModules(
featureModule,
unloadOnForgotten = true, // Unload when Composable leaves
unloadOnAbandoned = true // Unload if composition fails
)
}
OptionWhen Triggered
unloadOnForgottenComposable removed from composition
unloadOnAbandonedComposition fails or is abandoned

Use Cases

Feature Modules

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)
}

Lazy Feature Loading

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()
}
}

Debug/Preview Modules

Swap implementations for previews:

val debugModule = module {
single<ApiClient> { MockApiClient() }
}

@Preview
@Composable
fun FeatureScreenPreview() {
rememberKoinModules(debugModule)
FeatureScreen()
}

Conditional Modules

Load modules based on conditions:

@Composable
fun App(isDebug: Boolean) {
if (isDebug) {
rememberKoinModules(debugModule)
}

MainScreen()
}

With Lazy Modules

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>()
}

Best Practices

  1. Use unloadOnForgotten = true - prevents memory leaks

    rememberKoinModules(featureModule, unloadOnForgotten = true)
  2. One module per feature - keep modules focused and independent

  3. Combine with lazy modules - for large apps with many features

    val featureModule = lazyModule { /* ... */ }
  4. Load at navigation level - load modules in NavHost composables

  5. Avoid circular dependencies - feature modules should not depend on each other

Next Steps