Compose Multiplatform Patterns
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
A skill your agent uses to decide when Jetpack Compose derivedStateOf is the right tool and when it is pure overhead.
$ npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install skydoves/compose-performance-skills choosing-derivedstateof --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/skydoves/compose-performance-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/recomposition/choosing-derivedstateof .claude/skills/choosing-derivedstateof && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "choosing-derivedstateof" agent skill from https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateof into .claude/skills/choosing-derivedstateof/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "choosing-derivedstateof", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateofType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install skydoves/compose-performance-skills choosing-derivedstateof --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/compose-performance-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/recomposition/choosing-derivedstateof .agents/skills/choosing-derivedstateof && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "choosing-derivedstateof" agent skill from https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateof into .agents/skills/choosing-derivedstateof/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "choosing-derivedstateof", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install skydoves/compose-performance-skills choosing-derivedstateof --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/compose-performance-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/recomposition/choosing-derivedstateof .cursor/skills/choosing-derivedstateof && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "choosing-derivedstateof" agent skill from https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateof into .cursor/skills/choosing-derivedstateof/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "choosing-derivedstateof", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/skydoves/compose-performance-skills.git --path recomposition/choosing-derivedstateof--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install skydoves/compose-performance-skills choosing-derivedstateof --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/compose-performance-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/recomposition/choosing-derivedstateof .gemini/skills/choosing-derivedstateof && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "choosing-derivedstateof" agent skill from https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateof into .gemini/skills/choosing-derivedstateof/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "choosing-derivedstateof", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install skydoves/compose-performance-skills choosing-derivedstateofInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/skydoves/compose-performance-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/recomposition/choosing-derivedstateof .github/skills/choosing-derivedstateof && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "choosing-derivedstateof" agent skill from https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateof into .github/skills/choosing-derivedstateof/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "choosing-derivedstateof", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install skydoves/compose-performance-skills choosing-derivedstateof --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/compose-performance-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/recomposition/choosing-derivedstateof .opencode/skills/choosing-derivedstateof && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "choosing-derivedstateof" agent skill from https://github.com/skydoves/compose-performance-skills/tree/main/recomposition/choosing-derivedstateof into .opencode/skills/choosing-derivedstateof/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "choosing-derivedstateof", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
choosing-derivedstateofA skill your agent uses to decide when Jetpack Compose derivedStateOf is the right tool and when it is pure overhead.
Choosing Derivedstateof is an agent skill from skydoves/compose-performance-skills. Use this skill to decide when Jetpack Compose derivedStateOf is the right tool and when it is pure overhead. Covers the "input frequency must exceed output frequency" rule, the mandatory remember { derivedStateOf { } } wrapper, the canonical pitfall of capturing non-state variables by initial value (and the remember(key) fix), and the snapshotFlow alternative for fire-and-forget side effects on derived values. Use when the developer mentions derivedStateOf, scroll-position-driven booleans, threshold checks…
Its SKILL.md is about 2.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Mobile, covering Android development. It works with Jetpack Compose. The repository describes itself as: ⚡️ A curated library of Agent Skills focused on Jetpack Compose performance. The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 1b32f81. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).
From the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
medium.comdeveloper.android.comblog.zachklipp.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Choosing Derivedstateof loads about 2.9k tokens when it runs. Until then it costs about 179 tokens; SKILL.md has 985 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from skydoves/compose-performance-skills at commit 1b32f81, republished under its Apache-2.0 licence (© skydoves). 985 words, ~2,937 tokens.
.claude/skills/choosing-derivedstateof/SKILL.md (or your agent's skills folder).derivedStateOf produces a State whose readers only invalidate when the derived result changes, even if the input states change far more often. Use it when input frequency exceeds output frequency. Use it for any other shape and it is pure overhead — an extra snapshot subscription with no filtering benefit. This skill teaches Claude when to reach for it, when to refuse, and how to avoid the canonical capture-by-initial-value pitfall.
derivedStateOf.firstVisibleItemIndex == 0, scrollState.value > threshold, "show FAB on scroll").derivedStateOf exists in the code but recomposition counts didn't drop, or the value never updates after the first composition (the capture-by-initial-value bug).Modifier.alpha(state.value)). Fix that with ../deferring-state-reads/SKILL.md first — derivedStateOf does not address phase issues.../../stability/diagnosing-compose-stability/SKILL.md."$first $last" from two name states). derivedStateOf is pure overhead in that case — use a direct read or a plain remember.@TraceRecomposition from skydoves/compose-stability-analyzer. 1. Identify the input state(s) and the value the UI actually consumes. Write down both update frequencies. Example: listState.firstVisibleItemIndex (changes every list item scrolled past) → Boolean (changes once when the user crosses index 0).
2. Compare frequencies. Apply the rule:
derivedStateOf is the right tool.derivedStateOf adds an extra snapshot subscription for nothing. Use a direct read or a plain remember(input1, input2). 3. Wrap the derivation in remember { derivedStateOf { … } }. A bare derivedStateOf { } would be re-created on every composition, defeating the cache. The remember is mandatory.
4. If the lambda captures non-state variables (function parameters, locals, props), pass them as remember keys. A captured non-state variable is read once at first composition and frozen forever — the derivation will silently use the stale value. The fix is remember(threshold) { derivedStateOf { … > threshold } }.
5. For one-shot side effects on a derived value (logging, analytics, snackbar), prefer snapshotFlow { … }.collect { … } inside LaunchedEffect. That avoids subscribing the composable's restart scope to the derived state; the side effect runs in a coroutine, off the composition path.
6. Verify in Layout Inspector / @TraceRecomposition. The consuming composable's recomposition count MUST only increment when the derived result changes — not on every input tick. If it still climbs per input tick, either (a) the derivedStateOf is missing, (b) the remember is missing, or (c) something else (a sibling state read, a wrong-phase modifier) is invalidating the same scope.
// WRONG
val showFab = listState.firstVisibleItemIndex > 0
// WRONG because: this reads firstVisibleItemIndex on every recomposition; the consuming composable invalidates per scrolled item, not just when the boolean flips.// RIGHT
val showFab by remember {
derivedStateOf { listState.firstVisibleItemIndex > 0 }
}firstVisibleItemIndex updates on every list item scrolled past; showFab only flips when the user crosses index 0. The remember keeps the derived state alive across compositions; the derivedStateOf filters out every input change that does not flip the boolean.
// WRONG
val first by remember { mutableStateOf("Ada") }
val last by remember { mutableStateOf("Lovelace") }
val fullName by remember { derivedStateOf { "$first $last" } }
// WRONG because: first and last change at the same rate as fullName; derivedStateOf adds a snapshot subscription with zero filtering benefit.// RIGHT — direct read, no derivedStateOf needed
val first by remember { mutableStateOf("Ada") }
val last by remember { mutableStateOf("Lovelace") }
val fullName = "$first $last"If a memoization cost concern exists (the concatenation is expensive), use remember(first, last) { computeFullName(first, last) } — derivedStateOf is still the wrong shape because there is nothing to filter.
// WRONG
@Composable
fun Header(threshold: Int, listState: LazyListState) {
val isLarge by remember {
derivedStateOf { listState.firstVisibleItemIndex > threshold }
}
// WRONG because: threshold is captured inside remember { ... } at first composition; later threshold changes are ignored, and the derivation reuses the stale value forever.
}// RIGHT — threshold is a remember key, so a new derivedStateOf is created when it changes
@Composable
fun Header(threshold: Int, listState: LazyListState) {
val isLarge by remember(threshold) {
derivedStateOf { listState.firstVisibleItemIndex > threshold }
}
}The rule: any non-State value captured by the derivedStateOf lambda MUST be a key on the surrounding remember. Otherwise the derivation locks in the value from first composition.
snapshotFlow// WRONG
@Composable
fun Feed(listState: LazyListState, onScrolledPastFold: () -> Unit) {
val pastFold by remember {
derivedStateOf { listState.firstVisibleItemIndex > 5 }
}
if (pastFold) onScrolledPastFold()
// WRONG because: side effects in a composable body run on every (re)composition, can fire multiple times for the same flip, and tie the side effect to the composition lifecycle.
}// RIGHT — fire-and-forget side effect via snapshotFlow
@Composable
fun Feed(listState: LazyListState, onScrolledPastFold: () -> Unit) {
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex > 5 }
.distinctUntilChanged()
.filter { it }
.collect { onScrolledPastFold() }
}
}snapshotFlow reads snapshot state inside a coroutine and emits when the read-set's combined value changes. The LaunchedEffect keys the collection to listState, and distinctUntilChanged() ensures one emission per flip. No restart scope is involved — the consuming composable never recomposes for this signal.
// RIGHT
val priceBucket by remember {
derivedStateOf {
when {
cartTotal.value < 10_000 -> Bucket.Small
cartTotal.value < 50_000 -> Bucket.Medium
else -> Bucket.Large
}
}
}cartTotal may tick by single won/cents; priceBucket flips at most twice across the entire range. This is the canonical use case.
derivedStateOf in remember { … }. A bare derivedStateOf { } is re-created every composition and provides no filtering.remember keys. Otherwise the derivation freezes the values from first composition.derivedStateOf when input and output frequency match. It is pure overhead; use a direct read or remember(keys) { … }.collectAsState() / collectAsStateWithLifecycle() in derivedStateOf "just to be safe" — that adds a subscription layer for nothing. Filter upstream with .distinctUntilChanged() or .map { } on the flow.snapshotFlow { … } over derivedStateOf for fire-and-forget side effects. It keeps the composition free of the signal entirely.derivedStateOf is to reduce them; if they didn't drop, something else is wrong (often a wrong-phase read — see ../deferring-state-reads/SKILL.md).derivedStateOf in the file is inside a remember { … } (or remember(keys) { … }) call.derivedStateOf lambda's captured non-state variables appear as remember keys, OR the lambda only reads State objects.LaunchedEffect { snapshotFlow { … }.collect { } }, not by reading the derived state in the composable body.derivedStateOf { shows no occurrences where input and output frequencies match (no "$first $last"-shaped uses).© skydoves, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in recomposition/choosing-derivedstateof of skydoves/compose-performance-skills.
Open the folder on GitHubat commit 1b32f81
Choosing Derivedstateof next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Choosing Derivedstateof this skillskydoves/compose-performance-skills | 512 | — | ~2.9k | Automated safety check: Pass | Apache-2.0 | |
| Compose Multiplatform Patternsmonta-app/ocpp-emulator | 180 | 5 repos | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Deferring State ReadsrosuH/EasyWatermark | 1.9k | 1 repos | ~3.8k | Automated safety check: Pass | Apache-2.0 | |
| Android Developmentdpconde/claude-android-skill | 336 | — | ~1.7k | Automated safety check: Pass | MIT | |
| Footgun Scanmaxrave-dev/kotlin-footguns | 1k | 1 repos | ~518 | Automated safety check: Pass | GPL-3.0 | |
| Diagnosing Compose StabilityrosuH/EasyWatermark | 1.9k | 1 repos | ~3.3k | Automated safety check: Pass | Apache-2.0 |
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
rosuH/EasyWatermark
A skill your agent uses 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…
dpconde/claude-android-skill
Create production-quality Android applications following Google's official architecture guidance and NowInAndroid best practices.
maxrave-dev/kotlin-footguns
Scan a Kotlin or Compose Multiplatform diff for known footgun shapes and open the matching trap to confirm each hit.
rosuH/EasyWatermark
A skill your agent uses to diagnose Jetpack Compose stability problems by enabling and reading the Compose Compiler Reports (classes.txt, composables.txt, composables.csv, module.json).
arindamxd/camerax-android
A skill your agent uses to integrate the Jetpack Compose Styles API into an Android project.
skydoves/compose-performance-skills
A skill your agent uses to migrate Compose UI from collectAsState() to collectAsStateWithLifecycle(), hoist Flow<T parameters out of composables, and apply .conflate() / .distinctUntilChanged() /…
Works with
Categories
A skill your agent uses to decide when Jetpack Compose derivedStateOf is the right tool and when it is pure overhead. Choosing Derivedstateof is an agent skill from skydoves/compose-performance-skills. Use this skill to decide when Jetpack Compose derivedStateOf is the right tool and when it is pure overhead.
Choosing Derivedstateof fits situations like: decide when Jetpack Compose derivedStateOf is the right tool and when it is pure overhead; the developer mentions derivedStateOf; scroll-position-driven booleans; threshold checks.
Run `npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a claude-code`. Or copy the skill folder (recomposition/choosing-derivedstateof in skydoves/compose-performance-skills) into .claude/skills/choosing-derivedstateof in your project. Claude Code loads it when a task matches its description.
Run `npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a codex`. Or copy the skill folder (recomposition/choosing-derivedstateof in skydoves/compose-performance-skills) into .agents/skills/choosing-derivedstateof in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add skydoves/compose-performance-skills --skill choosing-derivedstateof -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/choosing-derivedstateof, .gemini/skills/choosing-derivedstateof, .github/skills/choosing-derivedstateof and .opencode/skills/choosing-derivedstateof in your project.
SKILL.md names no scripts, command-line tools or credentials: Choosing Derivedstateof is instructions for the agent only.
SKILL.md names 3 domains. As links in the text: medium.com, developer.android.com and blog.zachklipp.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Choosing Derivedstateof is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.9k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Choosing Derivedstateof: Compose Multiplatform Patterns (monta-app/ocpp-emulator, 180 stars), Deferring State Reads (rosuH/EasyWatermark, 1.9k stars), Android Development (dpconde/claude-android-skill, 336 stars) and Footgun Scan (maxrave-dev/kotlin-footguns, 1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
skydoves (a GitHub user) maintains it in skydoves/compose-performance-skills, which has 512 GitHub stars. The repository holds 2 skills in this directory. The repository was last updated on June 16, 2026.
Source: skydoves/compose-performance-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.