
Compose Performance Audit
- 458 installs
- 910 repo stars
- Updated July 27, 2026
- new-silvermoon/awesome-android-agent-skills
compose-performance-audit is a Claude Code skill that audits Jetpack Compose UI for recomposition waste, layout thrash, and scroll jank for developers who need smooth rendering on low-end Android devices before release.
About
compose-performance-audit is an Android agent skill from new-silvermoon/awesome-android-agent-skills that walks developers through a five-step Jetpack Compose performance workflow: code-first review, guided profiling, root-cause analysis, remediation, and verification. The skill starts by scanning composables for unstable parameters, missing LazyColumn keys, heavy work in composition blocks, and broad state reads that trigger recomposition storms. When code review is inconclusive, it directs developers to Android Studio Layout Inspector recomposition counts, Perfetto or System Trace frame timing, Recomposition Highlights, and Macrobenchmark scroll metrics on release builds with R8 enabled. Remediation guidance covers @Stable and @Immutable data classes, derivedStateOf, remember blocks, stable LazyList keys, and flattening layout hierarchies. Developers reach for compose-performance-audit when scrolling feels janky, recompositions spike, or Compose screens lag on low-end devices but the root cause is unclear between state, layout, or image loading.
- Recomposition hotspot detection
- Lazy list optimization
- Profiler-driven fixes
- Memory and jank checks
- Pre-release Compose tuning
Compose Performance Audit by the numbers
- 458 all-time installs (skills.sh)
- +14 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #314 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/new-silvermoon/awesome-android-agent-skills --skill compose-performance-auditAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 458 |
|---|---|
| repo stars | ★ 910 |
| Last updated | July 27, 2026 |
| Repository | new-silvermoon/awesome-android-agent-skills ↗ |
How do you fix janky Jetpack Compose scrolling and recompositions?
Audit Jetpack Compose UI for recomposition waste, layout thrash, and memory issues before release to keep scrolling smooth on low-end devices.
Who is it for?
Android developers shipping Jetpack Compose apps who see janky scrolling, recomposition storms, or slow rendering on low-end devices.
Skip if: Teams working in XML Views, Flutter, or React Native rather than Jetpack Compose, or projects without Android Studio profiling access.
When should I use this skill?
User reports slow Compose rendering, janky scrolling, excessive recompositions, or asks for a Compose performance audit before release.
What you get
Performance audit findings, recomposition root-cause analysis, remediation checklist, and verified Compose stability fixes for LazyColumn and state reads.
- performance audit report
- remediation checklist
- stability fixes
By the numbers
- Follows a five-step audit workflow from code review through verification
- Recommends four profiling tools: Layout Inspector, Perfetto, Recomposition Highlights, Macrobenchmark
Files
Compose Performance Audit
Overview
Audit Jetpack Compose view performance end-to-end, from instrumentation and baselining to root-cause analysis and concrete remediation steps.
Workflow Decision Tree
- If the user provides code, start with "Code-First Review."
- If the user only describes symptoms, ask for minimal code/context, then do "Code-First Review."
- If code review is inconclusive, go to "Guide the User to Profile" and ask for Layout Inspector output or Perfetto traces.
1. Code-First Review
Collect:
- Target Composable code.
- Data flow: state, remember, derived state, ViewModel connections.
- Symptoms and reproduction steps.
Focus on:
- Recomposition storms from unstable parameters or broad state changes.
- Unstable keys in
LazyColumn/LazyRow(keychurn, missing keys). - Heavy work in composition (formatting, sorting, filtering, object allocation).
- Unnecessary recompositions (missing
remember, unstable classes, lambdas). - Large images without proper sizing or async loading.
- Layout thrash (deep nesting, intrinsic measurements,
SubcomposeLayoutmisuse).
Provide:
- Likely root causes with code references.
- Suggested fixes and refactors.
- If needed, a minimal repro or instrumentation suggestion.
2. Guide the User to Profile
Explain how to collect data:
- Use Layout Inspector in Android Studio to see recomposition counts.
- Enable Recomposition Highlights in Compose tooling.
- Use Perfetto or System Trace for frame timing analysis.
- Check Macrobenchmark results for startup/scroll metrics.
Ask for:
- Layout Inspector screenshot showing recomposition counts.
- Perfetto trace or System Trace export.
- Device/OS/build configuration (debug vs release).
Important: Ensure profiling is done on a release build with R8 enabled. Debug builds have significant overhead.
3. Analyze and Diagnose
Prioritize likely Compose culprits:
- Recomposition storms from unstable parameters or broad state changes.
- Unstable keys in lazy lists (
keychurn, index-based keys). - Heavy work in composition (formatting, sorting, object allocation).
- Missing `remember` causing recreations on every recomposition.
- Large images without
Modifier.size()constraints. - Unnecessary state reads in wrong composition phases.
Summarize findings with evidence from traces/Layout Inspector.
4. Remediate
Apply targeted fixes:
- Stabilize parameters: Use
@Stableor@Immutableannotations on data classes. - Stabilize keys: Use stable, unique IDs for
LazyColumn/LazyRowitems. - Defer state reads: Use
derivedStateOf, lambda-based modifiers, orModifier.drawBehind. - Remember expensive computations: Wrap in
remember { }orremember(key) { }. - Skip recomposition: Extract stable composables, use
key()to control identity. - Async image loading: Use Coil/Glide with proper sizing constraints.
- Reduce layout complexity: Flatten hierarchies, avoid deep nesting.
Common Code Smells (and Fixes)
Unstable lambda captures
// BAD: New lambda instance every recomposition
Button(onClick = { viewModel.doSomething(item) }) { ... }
// GOOD: Use remember or method reference
val onClick = remember(item) { { viewModel.doSomething(item) } }
Button(onClick = onClick) { ... }Expensive work in composition
// BAD: Sorting on every recomposition
@Composable
fun ItemList(items: List<Item>) {
val sorted = items.sortedBy { it.name } // Runs every recomposition
LazyColumn { items(sorted) { ... } }
}
// GOOD: Use remember with key
@Composable
fun ItemList(items: List<Item>) {
val sorted = remember(items) { items.sortedBy { it.name } }
LazyColumn { items(sorted) { ... } }
}Missing keys in LazyColumn
// BAD: Index-based identity (causes recomposition on list changes)
LazyColumn {
items(items) { item -> ItemRow(item) }
}
// GOOD: Stable key-based identity
LazyColumn {
items(items, key = { it.id }) { item -> ItemRow(item) }
}Unstable data classes
// BAD: Unstable (contains List, which is not stable)
data class UiState(
val items: List<Item>,
val isLoading: Boolean
)
// GOOD: Mark as Immutable if truly immutable
@Immutable
data class UiState(
val items: ImmutableList<Item>, // kotlinx.collections.immutable
val isLoading: Boolean
)Reading state too early
// BAD: State read during composition (recomposes whole tree)
@Composable
fun AnimatedBox(scrollState: ScrollState) {
val offset = scrollState.value // Recomposes on every scroll
Box(modifier = Modifier.offset(y = offset.dp)) { ... }
}
// GOOD: Defer state read to layout/draw phase
@Composable
fun AnimatedBox(scrollState: ScrollState) {
Box(modifier = Modifier.offset {
IntOffset(0, scrollState.value) // Read in layout phase
}) { ... }
}Object allocation in composition
// BAD: Creates new Modifier chain every recomposition
Box(modifier = Modifier.padding(16.dp).background(Color.Red))
// GOOD for dynamic modifiers: Remember the modifier
val modifier = remember { Modifier.padding(16.dp).background(Color.Red) }
Box(modifier = modifier)Stability Checklist
| Type | Stable by Default? | Fix |
|---|---|---|
Primitives (Int, String, Boolean) | Yes | N/A |
data class with stable fields | Yes* | Ensure all fields are stable |
List, Map, Set | No | Use ImmutableList from kotlinx |
Classes with var properties | No | Use @Stable if externally stable |
| Lambdas | No | Use remember { } |
5. Verify
Ask the user to:
- Re-run Layout Inspector and compare recomposition counts.
- Run Macrobenchmark and compare frame timing.
- Test on a real device with release build.
Summarize the delta (recomposition count, frame drops, jank) if provided.
Outputs
Provide:
- A short metrics table (before/after if available).
- Top issues (ordered by impact).
- Proposed fixes with estimated effort.
References
Related skills
How it compares
Pick compose-performance-audit over generic Android lint skills when symptoms are Compose-specific recomposition or scroll jank rather than general APK size or network issues.
FAQ
What profiling tools does compose-performance-audit recommend?
compose-performance-audit recommends Android Studio Layout Inspector for recomposition counts, Recomposition Highlights, Perfetto or System Trace for frame timing, and Macrobenchmark for startup and scroll metrics on release builds with R8 enabled.
When should I start with code review versus profiling?
compose-performance-audit starts with code-first review when Compose source is available. If review is inconclusive, the skill guides profiling with Layout Inspector or Perfetto traces before mapping findings to remediation steps.
What Compose anti-patterns does compose-performance-audit fix?
compose-performance-audit targets unstable parameters, missing LazyColumn keys, heavy work in composition, broad state reads, and layout nesting. Fixes include @Stable annotations, derivedStateOf, remember blocks, and stable list item IDs.