Compose effects connect declarative UI to work outside composition. LaunchedEffect runs suspend work, SideEffect publishes successful composition results, and DisposableEffect registers something that must be unregistered.
Practical guidance
- Use LaunchedEffect for lifecycle-bound coroutines.
- Use DisposableEffect for listeners, observers and callbacks with cleanup.
- Use SideEffect to synchronize non-Compose state after composition succeeds.
- Choose keys that describe when the effect must restart.
Working example
LaunchedEffect(userId) { viewModel.load(userId) }
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event -> analytics.record(event) }
lifecycleOwner.lifecycle.addObserver(observer)
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
SideEffect { analytics.setScreenName(screenName) }
Common mistakes
- Launching a coroutine directly in a composable body starts it on every recomposition.
- Using Unit as a key creates an effect for the composable lifetime, not forever.
- DisposableEffect must release every resource it registers.
Key takeaway
Pick the API from the work’s lifecycle and cleanup needs. Effects are escape hatches, so keep them narrow and observable.
