Jetpack Compose
02 / 02

Recomposition Performance & Side Effects

Recomposition Performance & Side Effects

Skippability & Stability

A Composable can skip recomposing entirely if none of its parameters changed — but this requires all parameters to be "stable" (the compiler can reliably verify equality). A mutable var property, or an external class the compiler can't analyze, is "unstable" and breaks skippability. Mark a type @Stable or @Immutable to manually guarantee that contract.

State Hoisting

// stateless, reusable, easier to test/preview
@Composable
fun NameInput(value: String, onValueChange: (String) -> Unit) {
    TextField(value = value, onValueChange = onValueChange)
}

// caller owns the state
@Composable
fun Form() {
    var name by remember { mutableStateOf("") }
    NameInput(value = name, onValueChange = { name = it })
}

Hoisting state to the caller (value + onValueChange, rather than internal remember) keeps a component reusable — directly analogous to "lifting state up" in React.

Side Effects: LaunchedEffect & rememberCoroutineScope

LaunchedEffect(userId) {
    loadUserData(userId)   // restarts automatically when userId changes
}

val scope = rememberCoroutineScope()
Button(onClick = {
    scope.launch { saveData() }   // launched imperatively from a click, not automatically
}) { Text("Save") }

You can't call a suspend function directly in a Composable body — it would re-fire on every recomposition. LaunchedEffect ties a coroutine to the Composable's lifecycle and restarts it when its key changes; rememberCoroutineScope gives you a scope to launch manually from a callback, since LaunchedEffect can't be started imperatively.

derivedStateOf & Reading State Low

derivedStateOf{} recomputes a derived value but only triggers recomposition when that RESULT actually changes — useful when inputs (like scroll position) change far more often than the derived output (like isScrolledPastThreshold). Reading state as close as possible to where it's actually used (rather than high up the tree) helps Compose skip recomposing unrelated sibling branches.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free