— bash — netgod.dev — 80×24
guest@netgod.dev:~/blog$ cat jetpack-compose-performance-pitfalls.md
← cd ../blog
POST(ANDROID)netgod.dev manualPOST(ANDROID)
NAME

$ Jetpack Compose Performance: 7 Pitfalls I Hit in Production

DESCRIPTION

Compose makes it easy to write code that *looks* idiomatic and *runs* terribly. Here are the seven I now check for in every PR.

DATE
2025-03-25
DURATION
2 min read
TAGS
./assets/jetpack-compose-performance-pitfalls.pngcover
CONTENT

Compose is great. Compose is also a recomposition machine that will happily recompose your entire screen on every keystroke if you let it.

1. Reading state too high

Reading viewModel.uiState.collectAsState() in your top-level Scaffold recomposes everything. Push state reads down to the leaf that needs them.

2. Lambdas that capture state

// bad
Button(onClick = { count++ }) { ... }
// good — count is read inside the lambda, not captured
Button(onClick = onIncrement) { ... }

3. Lists without keys

items(list) re-keys on every change. items(list, key = { it.id }) enables structural identity.

4. Modifier chains as parameters

A fresh Modifier.padding(8.dp) is a new object every recomposition. Hoist it out or use remember.

5. derivedStateOf is not free

Use it when you have an expensive derivation read multiple times. Otherwise it adds overhead.

6. Animations driving recomposition

Modifier.offset { IntOffset(...) } (deferred read lambda) recomposes only the offset, not the whole composable. Modifier.offset(x = animatedValue) recomposes the whole tree.

7. Image loading on the main thread

Use Coil or Glide. Don't roll your own bitmap decoding inside a composable.

How I check

./gradlew assembleRelease -P composeCompilerReports=destination=... then read the module.txt report. If a function is restartable but not skippable, you have a problem.

netgod.dev manual2025-03-25END