How to Reduce Recomposition in Jetpack Compose (Beginner’s Guide)

If you’re starting with Jetpack Compose, you’ve probably heard the word Recomposition.
Sounds scary? Don’t worry. In this blog, we’ll break it down step by step — simple enough that even a beginner can master it.

By the end, you’ll know how to make your Jetpack Compose apps smooth, fast, and almost recomposition-free.


What is Recomposition in Jetpack Compose?

In Jetpack Compose, the UI is reactive. That means when the state (data) changes, Compose redraws the affected parts of the UI.

This redraw process is called Recomposition.
👉 Think of it like repainting only the wall where you added a new frame, not the entire house.

Good news: Recomposition is not bad.
Better news: You can control and reduce unnecessary recompositions.


Why Reduce Recomposition?

  • ✅ Faster UI performance
  • ✅ Less battery usage
  • ✅ Smooth scrolling in LazyColumn and lists
  • ✅ Professional-level code quality

🛠 Best Practices to Minimize Recomposition in Jetpack Compose

1. Use Stable and Immutable Data Classes

import androidx.compose.runtime.Immutable

@Immutable
data class UserUi(
    val id: Int,
    val name: String,
    val avatarUrl: String
)
  • Immutable = never changes internally → Compose can safely skip recomposition.
  • Annotate with @Immutable or @Stable when possible.

2. Avoid Creating New Objects in Composables

❌ Bad: Creates a new object every recomposition

UserCard(user = UserUi(1, "Alex", "url"))

✅ Good: Remember or hoist the object

val user = remember { UserUi(1, "Alex", "url") }
UserCard(user)

3. Hoist State to the Top (State Hoisting)

Keep state at the highest level and pass only what children need.

@Composable
fun UserScreen(viewModel: UserViewModel) {
    val user by viewModel.user.collectAsState()
    UserCard(name = user.name, avatar = user.avatarUrl)
}

👉 Only the UserCard updates when user data changes.


4. Use remember and derivedStateOf Wisely

  • remember {} → Cache expensive objects.
  • derivedStateOf {} → Recompute only when dependencies change.
@Composable
fun SearchResults(query: String, list: List<String>) {
    val filtered by remember(query, list) {
        derivedStateOf { list.filter { it.contains(query) } }
    }
    ResultsList(filtered)
}

5. Stable Lambdas with rememberUpdatedState

Passing lambdas directly can trigger recomposition.
Use stable references:

@Composable
fun ActionButton(onClick: () -> Unit) {
    Button(onClick = onClick) { Text("Click Me") }
}

ActionButton(onClick = viewModel::onSubmit) // Stable reference

If your lambda captures changing values:

val latestAction by rememberUpdatedState(newAction)
Button(onClick = { latestAction() }) { Text("Safe Click") }

6. Optimize Lists with Keys

When working with LazyColumn or LazyRow:

LazyColumn {
    items(users, key = { it.id }) { user ->
        UserCard(user)
    }
}

✅ Stable keys = only changed rows recompose, not the whole list.


7. Cache Heavy Objects in Modifiers

val brush = remember { Brush.linearGradient(colors) }
Box(Modifier.background(brush))

Saves Compose from recreating expensive objects every frame.


Tools to Detect Recomposition

  • Layout Inspector → Composition tab → See recomposition counts.
  • Perfetto + Compose Tracing → Timeline of recompositions.
  • Benchmarking → Test smoothness & performance.

Quick Checklist (Stick on Your Desk!)

  • Use @Immutable or stable data
  • Hoist state to the parent
  • Cache with remember
  • Use derivedStateOf for computed values
  • Stable lambdas (::function or rememberUpdatedState)
  • Provide stable keys in Lazy lists

Final Thoughts

You cannot remove recomposition completely — it’s the heart of Jetpack Compose.
But you can make your UI almost recomposition-free by writing stable, state-friendly, and well-structured code.

If you follow these tips, your Jetpack Compose apps will feel buttery smooth, professional, and ready for production.

programmer

Grab This On Amazon

Leave a Comment

Your email address will not be published. Required fields are marked *