The Silent Performance Killer: How a Single Lambda Nearly Destroyed My Compose UI
Article Summary
Raditya Gumay from GoTo discovered his navigation UI was recomposing 60+ times per minute. The culprit? A single lambda inside a data class that looked completely innocent.
While building navigation controls for GoTo's app, a Sr. Principal Engineer uncovered a subtle Jetpack Compose performance trap. Despite all state appearing stable in logs, the UI was recomposing every single second, wasting CPU cycles and draining battery.
Key Takeaways
- Lambdas in data classes use referential equality, creating new instances on every recomposition
- Custom equals() that ignores lambdas stopped the recomposition storm immediately
- Separating state from actions (no lambdas in state classes) is the cleanest solution
- 60+ unnecessary recompositions per minute eliminated with proper stability contracts
A data class containing a lambda caused continuous recomposition because Compose saw each lambda recreation as a state change, fixed by implementing type-based equality or separating state from actions entirely.
About This Article
Raditya Gumay found that CartoNavigationControls was recomposing every second even though the collected state had identical hash codes. This turned out to be a stability contract violation in Jetpack Compose.
He added custom equals() and hashCode() methods that skip lambda properties and used the @Stable annotation. This switched the comparison to type-based equality instead of checking the lambda functions structurally.
The fix cut unnecessary recompositions from 60+ per minute down to nearly zero, restored 60fps rendering, and reduced CPU usage and battery drain from wasted recomposition work during navigation.