One of Jetpack Compose’s most powerful features is its ability to automatically update the UI when data changes. This behavior is known as recomposition. Understanding how and when Compose recomposes is essential to writing efficient, predictable, and bug-free UI code.
Recomposition is the process where Jetpack Compose re-executes composable functions to update the UI in response to changes in state or data. Rather than redrawing everything, Compose intelligently updates only the parts of the UI that need to change.
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: $count")
Button(onClick = { count++ }) {
Text("Increase")
}
}
}
Each time the button is clicked, the count value changes — triggering
a recomposition. Compose then re-invokes the Counter() function, but
efficiently updates only the affected elements.
When you declare UI with composables, Compose builds a composition tree. During recomposition:
Compose automatically resets state when recomposing unless you explicitly
remember it. The remember function stores state in memory
so it survives recompositions.
@Composable
fun RememberExample() {
var text by remember { mutableStateOf("Hello") }
Column {
Text(text)
Button(onClick = { text = "Updated!" }) {
Text("Change Text")
}
}
}
Without remember, the text variable would reset to
"Hello" on every recomposition — breaking the reactive behavior.
Compose is smart enough to skip recomposition for composables whose parameters haven’t changed. This is part of its optimization mechanism.
@Composable
fun GreetingCard(name: String) {
Text("Hello, $name")
}
// If 'name' doesn't change, GreetingCard won't recompose.
To further optimize, you can use functions like
derivedStateOf or mark stable objects with
@Stable for advanced use cases.
Sometimes you need to perform tasks outside the scope of UI drawing, such as logging, network calls, or animations. These are handled using side-effect APIs that are lifecycle-aware.
LaunchedEffect — runs suspend functions during composition.DisposableEffect — cleans up when composable leaves composition.SideEffect — runs every time after successful recomposition.
@Composable
fun TimerExample() {
var time by remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
while (true) {
delay(1000)
time++
}
}
Text("Time elapsed: $time s")
}
Here, LaunchedEffect(Unit) ensures the timer starts once when the
composable enters the composition and continues running while it stays active.
Every composable follows a short lifecycle similar to Activity/Fragment lifecycles:
Enter Composition → Recompose (many times) → Leave Composition
You can observe these events using DisposableEffect:
@Composable
fun LifecycleAwareComposable() {
DisposableEffect(Unit) {
println("Entered composition")
onDispose {
println("Left composition")
}
}
}
remember for state retention within a composable.remember blocks.SideEffect or LaunchedEffect for non-UI operations.remember for mutable states.
With recomposition and lifecycle covered, the next article will focus on
State Management in Compose — using
remember, mutableStateOf, and other tools
to manage dynamic UI updates effectively.