
Deferring State Reads
Use this skill to push frequently-changing Jetpack Compose state reads (scroll...
Install
npx skills add skydoves/compose-performance-skills --skill deferring-state-reads -g -a claude-code -y- Repository
- skydoves/compose-performance-skills524
- Category
- Design
- Updated
- 5mo ago
- License
- Apache-2.0
- Context cost
- Mediumabout 3,000 tokens
- Runs scripts
- No, instructions only
What it does
Use this skill to push frequently-changing Jetpack Compose state reads (scroll position, animation values, drag offsets) out of the Composition phase and down into Layout or Draw using lambda-based modifiers like Modifier.offset { }, Modifier.layout { }, Modifier.graphicsLayer { }, Modifier.drawBehind { }, and Modifier.drawWithCache { }. Covers the three-phase model (Composition, Layout, Draw), why a state read at phase N invalidates phase N and every phase below, the modifier-phase cheat sheet, and lambda providers (() -> T) for hoisting hot values across composables. Use when the developer mentions every-frame work, scroll jank, animation jank, dropped frames, animated alpha or offset, "the whole subtree recomposes on scroll", Modifier.alpha(state.value), Modifier.offset(x.dp), or graphicsLayer.
Deferring State Reads, Move Hot Reads from Composition to Draw
Compose runs three phases per frame, Composition → Layout → Draw. A state read at phase N invalidates phase N and every phase below it. The single biggest perf win in any animation- or scroll-driven UI is moving a state read from Composition down to Layout or Draw via a lambda-based modifier. This skill teaches Claude how to spot the wrong-phase read and migrate it.
When to use this skill
- An animation triggers full subtree recomposition on every frame (
Modifier.alpha(progress.value),Modifier.offset(x.dp),Modifier.padding(state.dp),paddinghas no lambda overload, so useModifier.layout { ... }orModifier.offset { IntOffset(...) }instead when the inset is animated). - A scroll position is read directly in a composable body (
val y = scrollState.value) and the parent recomposes on every pixel of scroll. - The developer says "every frame", "scroll jank", "animation jank", "dropped frames", or reports the whole screen recomposing on drag.
- A
@TraceRecompositionlog shows the parent's recomposition counter incrementing once per animation tick. - The developer is passing a hot value (animation progress, scroll offset, drag delta) as a
Floatparameter across composables.
When NOT to use this skill
- The state changes once per user interaction (button click, dialog open), the lambda-modifier rewrite buys nothing and adds noise.
- The state read genuinely needs to drive Composition (show/hide a different composable, swap a different component tree). Lambda modifiers cannot decide which composable to emit.
- The non-skippable parent is non-skippable for a different reason (unstable parameter). Diagnose with
../../stability/diagnosing-compose-stability/SKILL.mdfirst. - The developer wants to filter many high-frequency inputs into one rare boolean, that is
../choosing-derivedstateof/SKILL.md.


