avoiding-subcomposition-pitfalls

Installation
SKILL.md

Avoiding Subcomposition Pitfalls — Keep Composition Out of the Measure Pass

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.

When to use this skill

  • 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).
  • A BoxWithConstraints or Scaffold is nested inside another BoxWithConstraints or Scaffold, multiplying subcomposition cost.
  • A BoxWithConstraints sits inside a LazyColumn/LazyRow item and the developer reports scroll jank that did not exist before the wrap was added.
  • The developer manually authors a SubcomposeLayout block but only uses constraints to compute final positions, never to drive child composition.
  • 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).
  • 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.

When NOT to use this skill

  • 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).
  • 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.
  • The state read driving repeated measurement is an animation value rather than a structural constraint. That is ../deferring-state-reads/SKILL.md.
  • The user wants to optimize lazy list prefetch — that is ../../lists/configuring-lazy-prefetch/SKILL.md.
Related skills

More from skydoves/compose-performance-skills

Installs
9
GitHub Stars
377
First Seen
Apr 29, 2026