Brifing

Your Compose App Isn't Slow - Your State Management Is

7 common state management pitfalls in Android Compose and how to fix them

ProAndroidDev - Medium3 min

1Impact of State Reading Mistakes

Most performance issues in Compose stem from reading state in the wrong place. The framework is rarely the bottleneck. When state changes, the nearest restartable scope where it's read gets invalidated. Therefore, state reading locations are critical to avoid unnecessary recompositions. For example, every keystroke in a search bar can recompose the entire screen.

2Reading State Too High in Tree

Reading state at high levels causes unnecessary recomposition of child components. If a search query is read at the top of a profile screen, every keystroke will recompose the header, post grid, and statistics. The solution is to push state reads down to the smallest scope that needs them. Using lambdas defers the read to child components, solving this issue.

3Reading Animation Values in Composition

Reading animation values that change every frame during composition phase runs the full pipeline unnecessarily. This triggers 60-120 recompositions per second. Lambda-based modifiers (like offset, alpha, scale) defer reads to layout or draw phases, skipping composition entirely. For example, using Modifier.offset{IntOffset(...)} instead of Modifier.offset(x,y) dramatically improves performance.

4Recomposing on Every Scroll Pixel

Directly reading continuously changing state like scroll position triggers recomposition on every pixel movement. Using LazyListState.firstVisibleItemIndex directly in a condition hurts performance. derivedStateOf converts rapidly changing state into meaningful changes. This way, only real changes (like first item visibility) trigger recomposition.

5Unstable Parameters and Skipping

Compose's strong skipping feature avoids recomposition when parameters don't change. However, unstable parameters break this optimization. A class is considered unstable if not all properties are marked val or if it contains vars. Using stable parameters and immutable data structures is crucial to prevent unnecessary recompositions.

6Giant Single State Object

Using one UiState for the entire screen causes changes in one area to affect the whole screen. State should be split into logical parts along recomposition boundaries. For example, a profile screen could have separate search state, user info state, and post list state. This way changes only affect relevant components.

7Backwards Writes

Writing to state that was just read during composition can cause infinite recomposition loops. This 'backwards write' error is a critical state management concern. The solution is to separate state reading and writing, performing side effects through controlled means like LaunchedEffect or callbacks. This mistake is common in animations and user interactions.

8Guessing Instead of Measuring

The biggest mistake in performance issues is optimizing without examining recomposition counts and composition traces. Layout Inspector and Composition Tracing tools are vital for detecting unnecessary recompositions. First measure to identify bottlenecks, then apply targeted optimizations. Random optimizations can sometimes worsen performance.

9Mental Model: Understanding Scopes

Compose recomposes scopes, not functions. The location of state reads determines which scope gets invalidated. This mental model forms the foundation of all optimizations. Limiting state reads to the pixel area actually affected by changes is key to performance. Understanding this model makes other mistakes almost obvious.

10Power of Lambda Usage

Deferring state reads to lambdas has the highest leverage in Compose optimizations. It should only be applied to frequently changing state (text input, scroll position, animation values). Lambdas shift reads to child components, preventing unnecessary parent recompositions. This technique offers high performance gains with very low code cost.

11Using Phases Correctly

Compose has three phases: composition, layout, and draw. State that changes every frame (like animation values) shouldn't be read during composition. Lambda-based modifiers allow deferring reads to layout or draw phases, skipping composition. For example, prefer lambda overloads for offset, alpha, and scale animations.

12Conclusion and Core Principles

Compose performance optimization fundamentally involves optimizing state read locations. All seven common mistakes share the same core principle: limit state reads to the pixel area actually affected by changes. These optimizations don't require learning new APIs - all techniques work in stable versions. When facing performance issues, always measure first, then apply targeted optimizations.

Original article: ProAndroidDev - Medium — Compose Uygulamanız Yavaş Değil, State Yönetiminiz Problemli