# Deferring State Reads

Use this skill to push frequently-changing Jetpack Compose state reads (scroll...

- Repository: https://github.com/skydoves/compose-performance-skills (524 stars)
- Category: Design
- License: Apache-2.0
- Last updated: 2026-06-16
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add skydoves/compose-performance-skills --skill deferring-state-reads -g -a claude-code -y`
Codex: `npx skills add skydoves/compose-performance-skills --skill deferring-state-reads -g -a codex -y`
Cursor: `npx skills add skydoves/compose-performance-skills --skill deferring-state-reads -g -a cursor -y`
Gemini CLI: `npx skills add skydoves/compose-performance-skills --skill deferring-state-reads -g -a gemini-cli -y`

## Description

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.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# 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

The rest: https://github.com/skydoves/compose-performance-skills/blob/HEAD/recomposition/deferring-state-reads/SKILL.md
