Skills

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
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), padding has no lambda overload, so use Modifier.layout { ... } or Modifier.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 @TraceRecomposition log 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 Float parameter 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.md first.
  • The developer wants to filter many high-frequency inputs into one rare boolean, that is ../choosing-derivedstateof/SKILL.md.

Prerequisites

Read the whole SKILL.md on GitHub

More in Design