Recomposition & Lifecycle in Jetpack Compose

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.

1. What is Recomposition?

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.

2. How Recomposition Works

When you declare UI with composables, Compose builds a composition tree. During recomposition:

Recomposition Flow:

  1. UI is first composed → initial layout created.
  2. State changes → Compose marks affected functions.
  3. Only those composables re-execute (recompose).
  4. Updated layout is efficiently re-rendered.

3. Key Concept: “Remember” and State

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.

4. Skipping Unnecessary Recompositions

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.

5. Side Effects and Lifecycle Awareness

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.


@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.

6. Composition Lifecycle Overview

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

7. Best Practices for Recomposition

8. Common Mistakes

9. What’s Next?

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.