# Overcoming Common Performance Pitfalls in Jetpack Compose

Jetpack Compose has a dedicated optimization strategy, but we must remember several things while writing code. These points are like rules that we need to follow daily to improve performance and save our application from severe performance pitfalls.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">As of today, while writing this blog, “Strong Skipping Mode” is experimental, and is currently under testing. It can be a game changer shortly and it might be possible that we do not need to worry about most of the things (if enabled in code) that will be discussed in this blog. But since then (or maybe even after that), these rules will be very crucial for development in Compose. Read more about the Strong Skipping Mode <a target="_blank" rel="noopener" class="ba xx" href="https://medium.com/androiddevelopers/jetpack-compose-strong-skipping-mode-explained-cbdb2aa4b900" style="pointer-events: none">here</a>.</div>
</div>

## A Quick Recap to the Rendering

Jetpack Compose renders a frame in the following 3 phases: **Composition**, **Layout**, and **Drawing**. On a high level, the responsibilities of these phases are: “What to show”, “Where to show” and “How to show” respectively. In the **Composition phase**, the runtime executes the composable functions and generates a UI tree consisting of layout nodes, that contain all information for the next 2 phases. In the **Layout phase**, with the help of the UI tree information, each node’s size and location are determined in 2D space. The entire UI tree is traversed and each node does these: Measure its children if any, based on the measurements decide on its own size, and each child node is placed in 2D space relative to the nodes’s own position. In the **Draw phase**, each node is traversed again from the top of the tree to the bottom, and based on the size and position measurements from the previous phase, they are drawn on the screen.

These are repeated in every frame where data is changed. Also, you can skip some phase(s) if data is not changed. Compose tracks what state is read within each of them. This allows Compose to notify only the specific phases that need to perform work for each affected element of your UI. This is extremely important for us to understand because based on the code we are writing we can skip some phases in the rendering, which can improve performance.

## **A Quick Recap to Stability in Compose**

Compose considers types to be either `stable` or `unstable`. A type is **stable** if it is immutable, or if Compose can know whether its value has changed between recompositions (notify Composition upon mutating). A type is **unstable** if Compose can’t know whether its value has changed between recompositions. If a composable has **stable parameters** that have not changed, Compose skips it. If a composable has **unstable parameters**, Compose always recomposes it when it recomposes the component’s parent. We can use the [Compose compiler reports](https://developer.android.com/jetpack/compose/performance/stability/diagnose#compose-compiler), using which the Compose compiler can output the results of its stability inference for inspection. The compiler marks functions to be either `skippable` or `restartable`. A **skippable** function denotes Compose can skip it during recomposition if all its arguments are equal with their previous values. Normally a function is marked as skippable if all its parameters are `stable` and thus Compose can infer when it has or has not changed. A **restartable** function denotes that this composable can serve as a restarting “scope”. This means that whenever this Composable needs to recompose, it will not trigger the recomposition of its parent scope. Rather this function itself can be a point of entry for where Compose can start re-executing code for recomposition after state changes.

## **Rules to Overcome Common Pitfalls**

### **1\. Avoid using unstable collections as much as possible**

The Compose compiler cannot be completely sure that collections such as `List`, `Map`, and `Set` are truly immutable and therefore mark them as unstable. This means, that if we use unstable collections as an argument in a composable function, the argument will be marked as unstable. This even the argument does not change, the composable will recompose if the parent recomposes.

**Consider this example to understand:** Here when the `FavoriteButton` was toggled, the list `articles` would also be recomposed as it has an **unstable** parameter type: `List`. Even if the `Article` class is stable in this example, that won’t help because the List is **unstable**.

```kotlin
@Composable
fun UnstableListScreen(viewModel: ListViewModel = viewModel()) {
    var favorite by remember { mutableStateOf(false) }
    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        FavoriteButton(isFavorite = favorite, onToggle = { favorite = !favorite })
        Spacer(modifier = Modifier.height(16.dp))
        UnstableList(viewModel.articles)
    }
}

@Composable
private fun UnstableList(
    articles: List<Article>, // List = Unstable, Article = Stable
    modifier: Modifier = Modifier // Stable
) {
    LazyColumn(modifier = modifier) {
        items(articles) { article ->
            Text(text = article.name)
        }
    }
}
```

**Overcome:**

**Solution 1:** To overcome this issue, we can use the [Kotlinx Immutable collections](https://github.com/Kotlin/kotlinx.collections.immutable) instead of using the default collections. Here the same code is modified by using the `PersistentList` from the Immutable Collections library as a replacement of `List`. This makes `articles` as **stable**. Hence, when the `FavoriteButton` was toggled, the list of articles was not recomposed.

```kotlin
@Composable
fun StableListScreen(viewModel: ListViewModel = viewModel()) {
    var favorite by remember { mutableStateOf(false) }
    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        FavoriteButton(isFavorite = favorite, onToggle = { favorite = !favorite })
        Spacer(modifier = Modifier.height(16.dp))
        StableList(viewModel.articles)
    }
}

@Composable
private fun StableList(
    articles: PersistentList<Article>, // PersistentList = Stable, Article = Stable
    modifier: Modifier = Modifier // Stable
) {
    LazyColumn(modifier = modifier) {
        items(articles) { article ->
            Text(text = article.name)
        }
    }
}
```

**Solution 2:** Though Solution 1 is a much more straightforward approach, one thing to consider here is the library is still in Alpha. So if we are skeptical about using this library in production code, we can use another approach: Using an Immutable wrapper around a standard List.

We should also follow this approach if the type of class used in the List cannot be made stable. Thus even if the `List` or `Article` is **unstable**, `ArticleList` will be **stable** because we are using the `@Immutable` annotation.

```kotlin
@Immutable
data class ArticleList(
    val articles: List<Article> // List = Unstable or Article = Unstable
)
```

Then instead of using the `List` directly in the above code, we can use this `ArticleList`.

```kotlin
@Composable
private fun StableList(
    articles: ArticleList, // ArticleList = Stable
    modifier: Modifier = Modifier // Stable
) {
    LazyColumn(modifier = modifier) {
        items(articles) { article ->
            Text(text = article.name)
        }
    }
}
```

### **2\. Remember the code inside the clickable() Modifier**

If we use the `clickable()` modifier on a composable, the lambda `onClick` of each item is reallocated every time the parent recomposes. This is because the object of the lambda is not auto-remembered.

**Consider this example to understand:** Here on adding each item to the `LazyColumn` (on the press of the `Button`), the entire list including the previous items is recomposed. This is because we have the `clickable()` modifier on each item of the list. So the lambda `onClick` of each item is reallocated every time the `LazyColumn` recomposes. This is because the object of the lambda is **not remembered**. This might cause serious performance issues as the remaining items on the list have no reason to recompose unnecessarily.

```kotlin
@Composable
fun ListWithNonRememberedClickableItems(viewModel: ListViewModel = viewModel()) {
    val dynamicList by viewModel.dynamicArticles.collectAsState()
    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        Button(
            onClick = { viewModel.addArticleToDynamicList() }
        ) {
            Text(text = "Add item")
        }
        Spacer(modifier = Modifier.height(32.dp))
        ListWithNonRememberedClickableItems(dynamicList)
    }
}

@Composable
private fun ListWithNonRememberedClickableItems(
    articles: List<Article>,
    modifier: Modifier = Modifier
) {
    val context = LocalContext.current
    LazyColumn(modifier = modifier) {
        items(articles) { article ->
            Text(
                modifier = Modifier.clickable { // Not remembered
                    Toast.makeText(context, "Clicked item: ${article.id}", Toast.LENGTH_SHORT).show()
                },
                text = article.name
            )
        }
    }
}
```

**Overcome:** To overcome this issue, we can wrap the `clickable()` Modifier inside a `remember` block. Thus the lambda object will be **remembered** and the won’t be reallocated every time the parent recomposes.

```kotlin
@Composable
fun ListWithRememberedClickableItems(
    articles: List<Article>,
    modifier: Modifier = Modifier
) {
    val context = LocalContext.current
    LazyColumn(modifier = modifier) {
        items(articles) { article ->
            Text(
                modifier = Modifier.then(
                    remember {
                        Modifier.clickable { // Remembered
                            Toast.makeText(context, "Clicked item: ${article.id}", Toast.LENGTH_SHORT).show()
                        }
                    }
                ),
                text = article.name
            )
        }
    }
}
```

**Consider another simple example:** Here typing anything in the `TextField` will recompose the parent composable function. But the `Text` composable “Toggle me” has nothing to do with it. Still, it will recompose, as it is using the `clickable()` Modifier, which is **not** **remembered**.

```kotlin
@Composable
fun ChildWithNonRememberedClickableModifier() {
    var text by remember { mutableStateOf("") }
    var isClicked by remember { mutableStateOf(false) }
    Column(modifier = Modifier.padding(24.dp)) {
        Text(
            modifier = Modifier,
            text = "Toggle state: $isClicked"
        )
        Spacer(modifier = Modifier.height(8.dp))
        Text(
            modifier = Modifier
                .padding(8.dp)
                .clickable { isClicked = !isClicked },
            text = "Toggle me"
        )
        Spacer(modifier = Modifier.height(16.dp))
        TextField(value = text, onValueChange = { text = it })
    }
}
```

**Overcome:** Similarly, to fix this issue, we can wrap the `clickable()` Modifier inside a `remember` block. Thus the lambda object will be **remembered** and the won’t be reallocated every time the parent recomposes.

```kotlin
@Composable
fun ChildWithRememberedClickableModifier() {
    var text by remember { mutableStateOf("") }
    var isClicked by remember { mutableStateOf(false) }
    Column(modifier = Modifier.padding(24.dp)) {
        Text(
            modifier = Modifier,
            text = "Toggle state: $isClicked"
        )
        Spacer(modifier = Modifier.height(8.dp))
        Text(
            modifier = Modifier
                .padding(8.dp)
                .then(
                    remember {
                        Modifier.clickable { isClicked = !isClicked }
                    }
                ),
            text = "Toggle me"
        )
        Spacer(modifier = Modifier.height(16.dp))
        TextField(value = text, onValueChange = { text = it })
    }
}
```

### **3\. Remember lamdas with calls on an unstable object**

This is very well explained by [Ben Trengrove](https://medium.com/@bentrengrove) in the “Unstable lambdas” section of the blog [Strong Skipping Mode Explained](https://medium.com/androiddevelopers/jetpack-compose-strong-skipping-mode-explained-cbdb2aa4b900). In short, as of now, the compose compiler does not auto-remember lambdas with unstable captures. And as discussed before if lambdas are **not remembered**, they will be reallocated every time the parent recomposes.

**Consider this example to understand**: Here as `ListViewModel` is unstable, the lambda `onValueChnage` containing the call `viewModel.numberChanged(it)` is **not auto-remembered** by the compiler. Thus when the parent recomposes due to the change in the `TextField` input, the `NumberComposable` also recomposes, even when it has nothing to do with the change of TextField.

```kotlin
@Composable
fun CallOnUnstableObjectWithNonRememberedLambda(viewModel: ListViewModel = viewModel()) { // ListViewModel is unstable
    val number by viewModel.number.collectAsState()
    var text by remember { mutableStateOf("") }
    Column(modifier = Modifier.padding(16.dp)) {
        NumberComposable(
            current = number,
            onValueChange = { viewModel.numberChanged(it) }
        )
        Spacer(modifier = Modifier.height(16.dp))
        TextField(value = text, onValueChange = { text = it })
    }
}
```

**Overcome:** To fix this problem, the `onValueChange` lambda content should be wrapped in a `remember` block.

```kotlin
@Composable
fun CallOnUnstableObjectWithRememberedLambda(viewModel: ListViewModel = viewModel()) { // ListViewModel is unstable
    val number by viewModel.number.collectAsState()
    var text by remember { mutableStateOf("") }
    Column(modifier = Modifier.padding(16.dp)) {
        NumberComposable(
            current = number,
            onValueChange = remember { { viewModel.numberChanged(it) } }
        )
        Spacer(modifier = Modifier.height(16.dp))
        TextField(value = text, onValueChange = { text = it })
    }
}
```

### **4\. Avoid using *background() Modifier while animating color***

Let’s think about the 3 phases of rendering we discussed at the start of this blog: **Composition**, **Layout**, and **Draw**. The `background()` modifier takes a color argument which is quite useful at times. But it’s also worth highlighting that this modifier causes all the 3 phases of rendering to run, even though it could have skipped the Composition and Layout phase (as the content or position of the Box is not changing, just the color is changing). This means if we are changing the argument `color` very frequently then heavy computation is done because the composable might recompose that frequently.

**Consider this example to understand**: Here the `background()` modifier is used on a `Box` composable, and the `color` is changed very frequently by using animation. As the color changes every 1 second, the `Box` recomposes every 1 second.

```kotlin
@Composable
fun CompositionInEveryPhase() {
    Box(
        modifier = Modifier.fillMaxSize().padding(16.dp)
    ) {
        var isNeedColorChange by remember { mutableStateOf(false) }
        val startColor = Color.Blue
        val endColor = Color.Green
        val backgroundColor by animateColorAsState(
            if (isNeedColorChange) endColor else startColor,
            animationSpec = tween(durationMillis = 800, delayMillis = 100, easing = LinearEasing),
            label = "Animate background color"
        )
        LaunchedEffect(Unit) {
            while (true) {
                delay(1000)
                isNeedColorChange = !isNeedColorChange
            }
        }
        Box(
            modifier = Modifier
                .size(300.dp)
                .align(Alignment.Center)
                .background(color = backgroundColor)
        )
    }
}
```

**Overcome:** To fix this issue, we can use the `drawBehind{}` lambda modifier and `drawRect()` function to draw the background color, which will only run the **Draw** phase, just skipping the first 2 phases.

```kotlin
@Composable
fun CompositionOnlyInDrawPhase() {
        // Remaining code
        Box(
            modifier = Modifier
                .size(300.dp)
                .align(Alignment.Center)
                .drawBehind {
                    drawRect(color = backgroundColor)
                }
        )
    }
}
```

### **5\. Avoid using transform modifiers directly while animating value**

We often need to perform transform animations in Compose (rotation, scale, position). While using direct transform modifiers like `Modifier.rotate()` if we change the value of the argument: `degrees`, then the entire composable will recompose. This means if we are changing the argument `degrees` very frequently then heavy computation is done because the composable might recompose that frequently. This recomposition is unnecessary since the appearance of the composable is not changing, we are just rotating it.

**Consider this example to understand:** Here we are using the rotate modifier directly on a `Box` composable, and changing the rotation `degrees` using an infinite animation. As a result, the `Box` recomposes whenever the value of the degree animates.

```kotlin
@Composable
fun TransformUsingRotateModifier() {
    Box(modifier = Modifier.fillMaxSize()) {
        val transition = rememberInfiniteTransition(label = "Infinite transition")
        val rotationDegree by transition.animateFloat(
            initialValue = 0f,
            targetValue = 1f,
            animationSpec = infiniteRepeatable(animation = tween(3000)),
            label = "Infinite animation"
        )
        Box(
            modifier = Modifier
                .align(Alignment.Center)
                .rotate(rotationDegree * 360f)
                .size(100.dp)
                .background(Color.Gray)
        )
    }
}
```

**Overcome:** To fix this performance caveat, we should use the `graphicsLayer{}` lambda modifier whenever possible. This Modifier only affects the draw phase, hence skipping the Composition and Layout phases, thus skipping recompositions. We should use it whenever possible while dealing with **clipping**, **transform**, **rotation**, or **alpha** changes.

```kotlin
@Composable
fun TransformUsingGraphicsLayerModifier() {
        // Remaining code
        Box(
            modifier = Modifier
                .align(Alignment.Center)
                .graphicsLayer {
                    rotationZ = rotationRatio * 360f
                }
                .size(100.dp)
                .background(Color.Gray)
        )
    }
}
```

---

That’s all for this article!

All the examples used in this blog are available in this repo:

%[https://github.com/pushpalroy/ComposePerformancePlayground] 

Use the “Layout Inspector” tool of Android Studio to run the examples and see the recompositions count, which will help a lot to understand.

> *I hope this writing will help many developers avoid these common pitfalls and write Jetpack Compose code with best practices in mind.*
