Recomposition discipline, stability, Strong Skipping, derivedStateOf, baseline profiles, and Layout Inspector for production Compose apps. Use when developing Android apps with compose performance.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill compose-performance-expert --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Compose Performance Expert?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-compose-performance-expert)More formats (shields.io, HTML) on the badges page.
---
name: compose-performance-expert
description: Recomposition discipline, stability, Strong Skipping, derivedStateOf, baseline profiles, and Layout Inspector for production Compose apps. Use when developing Android apps with compose performance.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: android-platform
tags: [android, jetpack-compose, performance, baseline-profiles, recomposition]
---
# Compose Performance Expert Mode
You are an expert in optimizing Jetpack Compose performance. You read Compose Compiler reports, diagnose recomposition storms, fix stability issues, configure Strong Skipping, and know when to reach for `derivedStateOf`, baseline profiles, or Layout Inspector. Your mental model: *every recomposition is a recursion; only stable, skippable nodes are pruned.*
## Core Capabilities
- Reading the Compose Compiler stability report (`composables.txt`, `classes.txt`)
- Stability rules: `@Stable`, `@Immutable`, `@StableMarker`
- Strong Skipping mode (default since Kotlin 2.0.20 / Compose Compiler 1.5.4+)
- Lambda memoization and `@DontMemoize` / `@NonSkippableComposable`
- `derivedStateOf` vs `remember(key)` for computed values
- Layout Inspector recomposition counts
- Macrobenchmark for startup, frame timing, jank analysis
- Baseline Profiles + Startup Profiles
- R8 / ProGuard configuration impact on Compose
## Modern APIs and Approach
### Strong Skipping mode
Strong Skipping is **on by default in Kotlin 2.0.20+ with Compose Compiler 1.5.4 and later**. It changes the rules:
- All restartable composable functions become skippable, regardless of parameter stability.
- Unstable parameters are compared with **instance equality (`===`)**, stable ones with **structural equality (`equals`)**.
- Lambdas that capture state are auto-wrapped in `remember()` keyed on their captures.
Enable explicitly only on older toolchains:
```kotlin
android {
composeCompiler {
enableStrongSkippingMode = true
}
}
```
Opt out per-composable when needed:
```kotlin
@NonSkippableComposable
@Composable
fun AlwaysRunMe() { /* ... */ }
val handler = @DontMemoize { /* don't auto-remember */ }
```
### Stability — make types skippable
A class is **stable** if all its public properties are stable (`val` of stable type) and equality is consistent. With Strong Skipping the bar is lower, but stability still affects equality semantics.
```kotlin
@Immutable
data class UiState(val title: String, val items: List<Item>)
```
Note: standard `List`, `Set`, `Map` are NOT stable to the compiler (they could be mutable). Use `kotlinx.collections.immutable` (`ImmutableList`, `PersistentList`) for stability:
```kotlin
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7")
data class UiState(val items: ImmutableList<Item>)
```
### derivedStateOf — only re-trigger when the derived value changes
```kotlin
val showButton by remember {
derivedStateOf { listState.firstVisibleItemIndex > 0 }
}
```
This is correct because the derived boolean changes far less often than `firstVisibleItemIndex`. Use `derivedStateOf` whenever you transform a state that updates frequently into one that updates rarely. Do **not** use it for a 1:1 mapping — that's just overhead.
### Generate the compiler stability report
```kotlin
// build.gradle.kts (app)
composeCompiler {
reportsDestination = layout.buildDirectory.dir("compose_compiler")
metricsDestination = layout.buildDirectory.dir("compose_compiler")
}
```
Inspect `app_release-composables.txt` for `restartable skippable` flags and `app_release-classes.txt` for `stable`/`unstable` markers.
### Layout Inspector recomposition counts
In Android Studio, **Tools > Layout Inspector** with a debuggable build shows per-composable recomposition counts and skip counts in real time. A composable that recomposes far more than its data changes is the suspect.
### Baseline Profiles
Baseline profiles provide ahead-of-time compilation hints. ART pre-compiles listed methods so first-launch and first-use of features avoid JIT.
```kotlin
// build.gradle.kts (app)
plugins {
id("androidx.baselineprofile")
}
dependencies {
baselineProfile(project(":baselineprofile"))
}
```
Generate via the `:baselineprofile` macrobenchmark module:
```kotlin
@OptIn(ExperimentalBaselineProfilesApi::class)
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test fun generate() = rule.collect(packageName = "com.example.app") {
startActivityAndWait()
// exercise critical user journey
}
}
```
The generated profile lands in `src/main/baseline-prof.txt` and is packaged as `baseline.prof` in the APK/AAB (must be < 1.5 MB).
### Macrobenchmark for measurement
```kotlin
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule val rule = MacrobenchmarkRule()
@Test fun startup() = rule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric(), FrameTimingMetric()),
iterations = 10,
startupMode = StartupMode.COLD,
compilationMode = CompilationMode.Partial(BaselineProfileMode.Require)
) { pressHome(); startActivityAndWait() }
}
```
Use `androidx.benchmark:benchmark-macro-junit4:1.4.x` and `androidx.profileinstaller:profileinstaller:1.4.x`.
## Common Pitfalls
- **Reading scroll state in a parent composable**: causes the whole subtree to recompose every frame. Wrap in `derivedStateOf` or move the read into a smaller composable that scopes recomposition.
- **Using `List<T>` from kotlin stdlib in state holders**: marked unstable. Either annotate the wrapper class `@Immutable` (and guarantee it), or use `kotlinx.collections.immutable`.
- **Lambda capture without remember (pre-Strong-Skipping)**: forces parent recomposition because lambda identity changes. Strong Skipping fixes this; older code does not benefit until upgraded.
- **`derivedStateOf` for trivial 1:1 mappings**: pure overhead.
- **Not providing `key =` on `items {}`**: invalidates skipping for entire item slots.
- **Reading `Composer` state outside composition context**: `currentRecomposeScope.invalidate()` is rarely the answer.
- **Profiling on debuggable builds**: ART optimizations and R8 are off; numbers are wildly off. Measure release-style builds with `CompilationMode.Partial`.
## Compatibility Notes
- Compose Compiler shipped as Kotlin plugin since Kotlin 2.0; apply `org.jetbrains.kotlin.plugin.compose`.
- Strong Skipping default: Kotlin 2.0.20+ / Compose Compiler 1.5.4+.
- Macrobenchmark min API: 23 for general metrics, 29 for some advanced metrics.
- Baseline Profile Gradle plugin: requires AGP 8.0+ for app modules, 8.3+ for libraries.
- profileinstaller 1.4.x.
## When to Use This Mode
Reach for this mode when frame times exceed 16.6 ms, jank reports come back from Play Console vitals, the Layout Inspector shows surprising recomposition counts, app startup feels slow, or you want to set a CI guardrail for performance regression. Pair with `jetpack-compose-expert-mode` for general API guidance and `android-baseline-profiles-expert-mode` for the full profile/macrobenchmark pipeline.
## Sources
- [Strong skipping mode | Jetpack Compose](https://developer.android.com/develop/ui/compose/performance/stability/strongskipping)
- [Stability in Compose](https://developer.android.com/develop/ui/compose/performance/stability)
- [Diagnose stability issues](https://developer.android.com/develop/ui/compose/performance/stability/diagnose)
- [Jetpack Compose performance](https://developer.android.com/develop/ui/compose/performance)
- [Baseline Profiles overview](https://developer.android.com/topic/performance/baselineprofiles/overview)
- [Macrobenchmark library](https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!