$npx -y skills add skydoves/compose-performance-skills --skill avoiding-subcomposition-pitfallsUse this skill when a Compose tree uses SubcomposeLayout, BoxWithConstraints, or Scaffold and the developer reports extra measure passes, slow first frame, or layout passes running content composition repeatedly. Covers why SubcomposeLayout composes its slots during the measure p
| 1 | # Avoiding Subcomposition Pitfalls — Keep Composition Out of the Measure Pass |
| 2 | |
| 3 | `SubcomposeLayout` runs its content's composition **during the measure pass**, not during the parent's composition pass. That trades extra layout cost for the ability to use measured constraints inside the children's composition. `BoxWithConstraints`, `material3.Scaffold`, and the lazy layouts are all built on top of it, so the cost is invisible until it nests or lands inside a hot path. This skill teaches Claude how to detect the misuse, how to replace `SubcomposeLayout` with a cheaper primitive when its power is not needed, and how to keep it efficient when it is. |
| 4 | |
| 5 | ## When to use this skill |
| 6 | |
| 7 | - The developer wraps content in `BoxWithConstraints` purely to read `maxWidth` / `maxHeight` and pick between a few composables, and the screen feels heavy at first frame or on configuration change (rotation, IME show). |
| 8 | - A `BoxWithConstraints` or `Scaffold` is nested inside another `BoxWithConstraints` or `Scaffold`, multiplying subcomposition cost. |
| 9 | - A `BoxWithConstraints` sits inside a `LazyColumn`/`LazyRow` item and the developer reports scroll jank that did not exist before the wrap was added. |
| 10 | - The developer manually authors a `SubcomposeLayout` block but only uses constraints to compute final positions, never to drive child composition. |
| 11 | - The Layout Inspector or a Perfetto trace shows `Compose:applyChanges` or measure-phase composition counts that scale with parent constraint changes (rotation, drag-to-resize, animated container size). |
| 12 | - A custom `SubcomposeLayout`'s `measurePolicy` block allocates a fresh composable lambda inside `subcompose(slotId) { … }` on every measurement. AndroidX maintains an internal lint check named `ComposableLambdaInMeasurePolicy` (in `compose/lint/internal-lint-checks/`) that flags exactly this pattern on its own codebase; published APIs like `BoxWithConstraints`, `material.Scaffold`, and `material3.TabRow` suppress that check because the trade-off is fundamental to their public contract. App-side code that reproduces the pattern pays the same cost without the suppression rationale. |
| 13 | |
| 14 | ## When NOT to use this skill |
| 15 | |
| 16 | - The developer needs to compose a child whose content depends on another child's measured size — that is the canonical `SubcomposeLayout` use case and there is no cheaper primitive. Keep `SubcomposeLayout`; tune it (see Pattern: Tuning a justified SubcomposeLayout). |
| 17 | - The slow path is inside a `LazyColumn` item but the cause is unstable parameters, not subcomposition. Diagnose with `../../stability/diagnosing-compose-stability/SKILL.md` first. |
| 18 | - The state read driving repeated measurement is an animation value rather than a structural constraint. That is `../deferring-state-reads/SKILL.md`. |
| 19 | - The user wants to optimize lazy list prefetch — that is `../../lists/configuring-lazy-prefetch/SKILL.md`. |
| 20 | |
| 21 | ## Prerequisites |
| 22 | |
| 23 | - Familiarity with Compose's three phases (Composition, Layout, Draw). If unsure, read `../deferring-state-reads/references/three-phases.md` first. |
| 24 | - A **release** build with R8 for any final measurement. `../../measurement/testing-compose-in-release-mode/SKILL.md` covers why debug builds lie. |
| 25 | - Layout Inspector or Perfetto trace tooling. `../../measurement/tracing-recompositions-at-runtime/SKILL.md` covers wiring `androidx.compose.runtime:runtime-tracing` so that measure-phase work shows up in system traces. |
| 26 | - A modern Compose UI version that exposes the `SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse: Int)` factory and pausable subcomposition on the lazy-list path. Both ship in the current `androidx.compose.ui:ui` / `androidx.compose.foundation:foundation` artifacts; check the release notes at https://developer.android.com/jetpack/androidx/releases/compose-ui and https://developer.android.com/jetpack/and |