React Native Best Practices
vercel-labs/openreview
A prioritized rule set for React Native and Expo apps covering list performance, animation, navigation, UI patterns, state, rendering, monorepos and configuration.
A skill your agent uses when a Compose tree uses SubcomposeLayout, BoxWithConstraints, or Scaffold and the developer reports extra measure passes, slow first frame, or layout passes running content…
$ npx skills add rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install rosuH/EasyWatermark avoiding-subcomposition-pitfalls --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/rosuH/EasyWatermark.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/avoiding-subcomposition-pitfalls .claude/skills/avoiding-subcomposition-pitfalls && 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 "avoiding-subcomposition-pitfalls" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfalls into .claude/skills/avoiding-subcomposition-pitfalls/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "avoiding-subcomposition-pitfalls", 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/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfallsType 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 rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install rosuH/EasyWatermark avoiding-subcomposition-pitfalls --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/avoiding-subcomposition-pitfalls .agents/skills/avoiding-subcomposition-pitfalls && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "avoiding-subcomposition-pitfalls" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfalls into .agents/skills/avoiding-subcomposition-pitfalls/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "avoiding-subcomposition-pitfalls", 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 rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install rosuH/EasyWatermark avoiding-subcomposition-pitfalls --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/avoiding-subcomposition-pitfalls .cursor/skills/avoiding-subcomposition-pitfalls && 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 "avoiding-subcomposition-pitfalls" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfalls into .cursor/skills/avoiding-subcomposition-pitfalls/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "avoiding-subcomposition-pitfalls", 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/rosuH/EasyWatermark.git --path .agents/skills/avoiding-subcomposition-pitfalls--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 rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install rosuH/EasyWatermark avoiding-subcomposition-pitfalls --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/avoiding-subcomposition-pitfalls .gemini/skills/avoiding-subcomposition-pitfalls && 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 "avoiding-subcomposition-pitfalls" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfalls into .gemini/skills/avoiding-subcomposition-pitfalls/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "avoiding-subcomposition-pitfalls", 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 rosuH/EasyWatermark avoiding-subcomposition-pitfallsInstalls 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 rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/avoiding-subcomposition-pitfalls .github/skills/avoiding-subcomposition-pitfalls && 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 "avoiding-subcomposition-pitfalls" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfalls into .github/skills/avoiding-subcomposition-pitfalls/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "avoiding-subcomposition-pitfalls", 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 rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install rosuH/EasyWatermark avoiding-subcomposition-pitfalls --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/avoiding-subcomposition-pitfalls .opencode/skills/avoiding-subcomposition-pitfalls && 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 "avoiding-subcomposition-pitfalls" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/.agents/skills/avoiding-subcomposition-pitfalls into .opencode/skills/avoiding-subcomposition-pitfalls/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "avoiding-subcomposition-pitfalls", 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.
avoiding-subcomposition-pitfallsA skill your agent uses when a Compose tree uses SubcomposeLayout, BoxWithConstraints, or Scaffold and the developer reports extra measure passes, slow first frame, or layout passes running content…
Avoiding Subcomposition Pitfalls is an agent skill from rosuH/EasyWatermark. Use this skill when a Compose tree uses SubcomposeLayout, BoxWithConstraints, or Scaffold and the developer reports extra measure passes, slow first frame, or layout passes running content composition repeatedly. Covers why SubcomposeLayout composes its slots during the measure phase, why BoxWithConstraints forces a subcomposition for every new Constraints value, why nesting Scaffold or BoxWithConstraints multiplies the cost, when a custom Layout or Modifier.layout { } replaces SubcomposeLayout, and how to use…
Its SKILL.md is about 4.7k 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. The repository describes itself as: 🔒 🖼 Securely, easily add a watermark to your sensitive photos. 安全、简单地为你的敏感照片添加水印,防止被人泄露、利用. The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 61223db. 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):
developer.android.comcs.android.comchrisbanes.mespeakerdeck.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.
Avoiding Subcomposition Pitfalls loads about 4.7k tokens when it runs. Until then it costs about 228 tokens; SKILL.md has 1,503 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 rosuH/EasyWatermark at commit 61223db, republished under its Apache-2.0 licence (© rosuH). 1,503 words, ~4,709 tokens.
.claude/skills/avoiding-subcomposition-pitfalls/SKILL.md (or your agent's skills folder).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.
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).BoxWithConstraints or Scaffold is nested inside another BoxWithConstraints or Scaffold, multiplying subcomposition cost.BoxWithConstraints sits inside a LazyColumn/LazyRow item and the developer reports scroll jank that did not exist before the wrap was added.SubcomposeLayout block but only uses constraints to compute final positions, never to drive child composition.Compose:applyChanges or measure-phase composition counts that scale with parent constraint changes (rotation, drag-to-resize, animated container size).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.SubcomposeLayout use case and there is no cheaper primitive. Keep SubcomposeLayout; tune it (see Pattern: Tuning a justified SubcomposeLayout).LazyColumn item but the cause is unstable parameters, not subcomposition. Diagnose with ../../stability/diagnosing-compose-stability/SKILL.md first.../deferring-state-reads/SKILL.md.../../lists/configuring-lazy-prefetch/SKILL.md.../deferring-state-reads/references/three-phases.md first.../../measurement/testing-compose-in-release-mode/SKILL.md covers why debug builds lie.../../measurement/tracing-recompositions-at-runtime/SKILL.md covers wiring androidx.compose.runtime:runtime-tracing so that measure-phase work shows up in system traces.SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse: Int) factory and pausable subcomposition on the lazy-list path. Both ship in the current androidx.compose.ui:ui / androidx.compose.foundation:foundation artifacts; check the release notes at https://developer.android.com/jetpack/androidx/releases/compose-ui and https://developer.android.com/jetpack/androidx/releases/compose-foundation if pinning to an older release. 1. Identify every SubcomposeLayout user in the hot path. Grep the affected screen for BoxWithConstraints, Scaffold, SubcomposeLayout, and any custom @Composable that delegates to one. The material3.Scaffold and foundation.layout.BoxWithConstraints are both implemented with SubcomposeLayout; their cost is identical.
2. For each user, decide whether the constraints are actually consumed during composition. A BoxWithConstraints that reads maxWidth only to pass it to a child as a parameter is wasting the subcomposition machinery. A BoxWithConstraints that picks between two completely different @Composable trees based on maxWidth < 600.dp is using it correctly.
3. Replace unjustified BoxWithConstraints with a cheaper primitive. If the consumer only needs the final size in pixels (e.g. to compute an offset, an alpha, a draw command), use Modifier.layout { measurable, constraints -> … } or Modifier.onSizeChanged { … } instead. Both run during the layout phase without spinning up a new composition.
4. Avoid nesting subcomposing layouts. Two Scaffolds nested, or a BoxWithConstraints inside a Scaffold slot that itself contains another BoxWithConstraints, multiplies measure-phase composition. Hoist the outer one to the screen root and use ordinary Box / Column / Row for the inner branches.
5. Never wrap a LazyColumn/LazyRow item in BoxWithConstraints. Each item subcomposes during measurement. If the item needs the available width, pass it down once from the parent (the LazyColumn already knows its constraints) or use Modifier.fillMaxWidth().onSizeChanged { … } for one-time sizing.
6. When SubcomposeLayout is justified, configure SubcomposeLayoutState(SubcomposeSlotReusePolicy(n)). The state object retains up to n previously-used slots so the next subcomposition reuses their slot table and node subtree instead of paying for a fresh composition. The factory SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse: Int) ships in androidx.compose.ui.layout.
7. For predictable upcoming slots, call state.precompose(slotId, content) on a previous frame. It returns a PrecomposedSlotHandle; the next subcompose(slotId, …) during measurement skips the composition step. This is exactly how the lazy layouts spread item composition across frames.
8. Verify in a Perfetto system trace. Add androidx.compose.runtime:runtime-tracing and confirm that Compose:recompose and Compose:applyChanges no longer fire during scroll or resize ticks at the rate of the parent's constraint changes. The measured screen should show subcomposition only on actual structural change (e.g. rotation), not on every frame.
BoxWithConstraints used to read size — replace with Modifier.onSizeChanged// WRONG
@Composable
fun ParallaxHeader(scrollOffset: () -> Float) {
BoxWithConstraints {
val widthPx = with(LocalDensity.current) { maxWidth.toPx() }
Image(
painter = painterResource(R.drawable.hero),
modifier = Modifier
.fillMaxWidth()
.height(220.dp)
.graphicsLayer {
translationX = scrollOffset() * widthPx * 0.2f
},
contentDescription = null,
)
}
}
// WRONG because: BoxWithConstraints subcomposes its content during the measure phase. Each
// new constraint (rotation, IME show, animated parent size) re-runs that subcomposition just
// to read maxWidth, when a Modifier.onSizeChanged would have given the same value with no
// extra composition.// RIGHT
@Composable
fun ParallaxHeader(scrollOffset: () -> Float) {
var widthPx by remember { mutableStateOf(0f) }
Image(
painter = painterResource(R.drawable.hero),
modifier = Modifier
.fillMaxWidth()
.height(220.dp)
.onSizeChanged { widthPx = it.width.toFloat() }
.graphicsLayer {
translationX = scrollOffset() * widthPx * 0.2f
},
contentDescription = null,
)
}onSizeChanged runs in the layout phase (no subcomposition); the underlying onRemeasured is invoked on every layout pass and forwards to the user callback when the measured size differs from the previous size. The graphicsLayer block runs in Draw with the latest widthPx and the latest scroll offset.
BoxWithConstraints to pick a layout — keep it but hoist it// WRONG
@Composable
fun ProductCard(product: Product) {
BoxWithConstraints {
if (maxWidth < 360.dp) {
CompactProductCard(product)
} else {
WideProductCard(product)
}
}
}
@Composable
fun ProductGrid(products: List<Product>) {
LazyVerticalGrid(columns = GridCells.Fixed(2)) {
items(products) { ProductCard(it) }
}
}
// WRONG because: every grid cell wraps its content in BoxWithConstraints. Each cell pays
// for a subcomposition during the grid's measure pass. With 200 cells visible across a long
// scroll session, that is 200 redundant subcompositions on every measurement.// RIGHT
@Composable
fun ProductGrid(products: List<Product>) {
BoxWithConstraints {
val isCompact = maxWidth / 2 < 360.dp
LazyVerticalGrid(columns = GridCells.Fixed(2)) {
items(products) { product ->
if (isCompact) CompactProductCard(product) else WideProductCard(product)
}
}
}
}The single BoxWithConstraints at the grid root subcomposes once per parent constraint change. The decision flows into every item as a plain Boolean, which is stable and skippable.
Scaffold — collapse to one// WRONG
@Composable
fun ProfileScreen(...) {
Scaffold(topBar = { TopBar(...) }) { outer ->
Scaffold(
modifier = Modifier.padding(outer),
bottomBar = { ProfileTabBar(...) },
) { inner ->
ProfileContent(modifier = Modifier.padding(inner))
}
}
}
// WRONG because: each Scaffold is a SubcomposeLayout with its own measure-phase composition.
// Nesting them doubles that cost on every frame whose constraints change (IME show, rotation,
// nav bar inset change).// RIGHT
@Composable
fun ProfileScreen(...) {
Scaffold(
topBar = { TopBar(...) },
bottomBar = { ProfileTabBar(...) },
) { padding ->
ProfileContent(modifier = Modifier.padding(padding))
}
}Scaffold already supports a top bar, bottom bar, snackbar host, and FAB in a single subcomposition. Use the slots provided rather than nesting another Scaffold.
SubcomposeLayoutWhen a screen genuinely needs measure-time access to constraints (a tab strip whose tabs size by their own content while sharing the row's max width, a lookahead-driven shared element container, a custom popover that aligns to a measured anchor), keep SubcomposeLayout but tune the state.
@Composable
fun BadgeRow(badges: List<Badge>) {
val slotState = remember {
SubcomposeLayoutState(SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse = 8))
}
SubcomposeLayout(state = slotState) { constraints ->
val measurables = subcompose(slotId = "badges") {
badges.fastForEach { Badge(it) }
}
val placeables = measurables.map { it.measure(constraints) }
val width = placeables.sumOf { it.width }
val height = placeables.maxOf { it.height }
layout(width, height) {
var x = 0
placeables.fastForEach { p ->
p.place(x, 0)
x += p.width
}
}
}
}SubcomposeSlotReusePolicy(8) lets the layout retain up to eight previously-composed slots. When a slot id reappears (e.g. a badge enters the screen again after exiting), the layout reuses the existing slot table instead of rerunning composition. The lazy layouts use a similar mechanism with their own policy implementation.
For predictable upcoming slots, call state.precompose(slotId, content) on an earlier frame and dispose the returned PrecomposedSlotHandle if the slot is not needed after all. The next subcompose(slotId, …) during measurement reuses the precomposed content with no composition step.
LazyListScope.item { } containing BoxWithConstraints// WRONG
LazyColumn {
items(rows) { row ->
BoxWithConstraints {
val isWide = maxWidth > 600.dp
RowContent(row, isWide)
}
}
}
// WRONG because: each row item subcomposes during the LazyColumn's measure pass. Scroll the
// list and every newly-visible item pays for a fresh subcomposition. On a 60Hz feed this is
// the difference between smooth and dropped frames.// RIGHT
@Composable
fun RowFeed(rows: List<Row>) {
BoxWithConstraints {
val isWide = maxWidth > 600.dp
LazyColumn {
items(rows, key = { it.id }) { row -> RowContent(row, isWide) }
}
}
}The single BoxWithConstraints at the feed root subcomposes only when the feed itself is remeasured. The per-row item is a plain RowContent call with a stable Boolean parameter, which strong skipping handles.
BoxWithConstraints only to read maxWidth/maxHeight for a Modifier-level effect. Use Modifier.onSizeChanged { … } or Modifier.layout { … } instead.BoxWithConstraints inside a LazyColumn/LazyRow/LazyVerticalGrid item. Hoist the constraint read to the lazy layout's parent and pass the resolved value down.Scaffold inside another Scaffold. Use the slots (topBar, bottomBar, snackbarHost, floatingActionButton) the outer Scaffold already exposes.SubcomposeLayout when the children's composition genuinely depends on a measured value (parent constraints, sibling size). The point is to use the right tool, not to ban SubcomposeLayout.SubcomposeSlotReusePolicy to SubcomposeLayoutState whenever slots come and go. The default no-op policy disposes slots eagerly and pays full composition cost on every reappearance.state.precompose(slotId, content) on a previous frame and let the measure-pass subcompose reuse the result.Modifier.onSizeChanged { … } over reading maxWidth in a BoxWithConstraints body when the value only feeds a graphicsLayer { }, drawBehind { }, or other Layout/Draw-phase consumer.androidx.compose.runtime:runtime-tracing shows that Compose:recompose and Compose:applyChanges no longer fire on every measurement of the affected subtree. They should fire only on real structural change (rotation, navigation, list item insert).BoxWithConstraints does not increment per parent constraint change.SubcomposeLayout, the subcompose(slotId) { … } content lambda is hoisted into a remember-stable reference (or is cheap and intentional, with a one-line comment explaining why a fresh lambda per measurement is safe here). This is the pattern AndroidX's internal ComposableLambdaInMeasurePolicy lint enforces on the AndroidX codebase.BoxWithConstraints blocks inside LazyColumn/LazyRow/LazyVerticalGrid items.Scaffold is nested inside another Scaffold in the migrated file.SubcomposeLayout, a SubcomposeSlotReusePolicy is passed to SubcomposeLayoutState, OR the comment explicitly notes that all slots are permanent and reuse is unnecessary.SubcomposeLayout reference: https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/package-summary#SubcomposeLayout(androidx.compose.ui.Modifier,kotlin.Function2)SubcomposeLayout.kt: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.ktBoxWithConstraints.kt: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/BoxWithConstraints.ktScaffold.kt (uses SubcomposeLayout for the chrome layout): https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Scaffold.kt../deferring-state-reads/references/three-phases.md — three-phase model background that motivates "do not run composition during measure".../../lists/configuring-lazy-prefetch/SKILL.md — the lazy layouts use precompose and pausable subcomposition; the same APIs are available to custom SubcomposeLayout users.© rosuH, 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 .agents/skills/avoiding-subcomposition-pitfalls of rosuH/EasyWatermark.
Open the folder on GitHubat commit 61223db
We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in rosuH/EasyWatermark, which our catalogue first saw on October 7, 2026.
Avoiding Subcomposition Pitfalls 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 |
|---|---|---|---|---|---|---|
| Avoiding Subcomposition Pitfalls this skillrosuH/EasyWatermark | 1.9k | 1 repos | ~4.7k | Automated safety check: Pass | Apache-2.0 | |
| React Native Best Practicesvercel-labs/openreview | 1.7k | 17 repos | ~1.1k | Automated safety check: Pass | MIT | |
| Swiftui Protwostraws/SwiftUI-Agent-Skill | 5k | 2 repos | ~1k | Automated safety check: Pass | MIT | |
| Ip As LogoKartikLabhshetwar/better-shot | 2.4k | 1 repos | ~4.3k | Automated safety check: Pass | MIT | |
| Compose Multiplatform Patternsmonta-app/ocpp-emulator | 179 | 5 repos | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Aso Appstore Screenshotsadamlyttleapps/claude-skill-aso-appstore-screenshots | 1.8k | 1 repos | ~9.6k | Automated safety check: Pass | MIT |
vercel-labs/openreview
A prioritized rule set for React Native and Expo apps covering list performance, animation, navigation, UI patterns, state, rendering, monorepos and configuration.
twostraws/SwiftUI-Agent-Skill
Comprehensively reviews SwiftUI code for best practices on modern APIs, maintainability, and performance.
KartikLabhshetwar/better-shot
Generate extremely simple, cute, personified square character images with rounded heavy forms, two purposeful character colors, one solid background color, and a dominant lower-corner composition.
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
adamlyttleapps/claude-skill-aso-appstore-screenshots
Generate high-converting App Store screenshots by analyzing your app's codebase, discovering core benefits, and creating ASO-optimized screenshot images using Nano Banana Pro.
AFK-surf/OpenBridge
Best practices and example-driven guidance for building SwiftUI views and components.
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…
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).
rosuH/EasyWatermark
A skill your agent uses to generate and measure Jetpack Compose Baseline Profiles end-to-end with the AGP 8.2+ Baseline Profile Generator module and the Macrobenchmark harness.
rosuH/EasyWatermark
A skill your agent uses to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T.
rosuH/EasyWatermark
Analyzes Android codebases to implement ML Kit GenAI Prompt API.
rosuH/EasyWatermark
A skill your agent uses to fix unstable Jetpack Compose types once a stability diagnosis has identified them.
Categories
A skill your agent uses when a Compose tree uses SubcomposeLayout, BoxWithConstraints, or Scaffold and the developer reports extra measure passes, slow first frame, or layout passes running content…. Avoiding Subcomposition Pitfalls is an agent skill from rosuH/EasyWatermark. Use this skill when a Compose tree uses SubcomposeLayout, BoxWithConstraints, or Scaffold and the developer reports extra measure passes, slow first frame, or layout passes running content composition repeatedly.
Avoiding Subcomposition Pitfalls fits situations like: A Compose tree uses SubcomposeLayout; boxWithConstraints; scaffold and the developer reports extra measure passes; slow first frame.
Run `npx skills add rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a claude-code`. Or copy the skill folder (.agents/skills/avoiding-subcomposition-pitfalls in rosuH/EasyWatermark) into .claude/skills/avoiding-subcomposition-pitfalls in your project. Claude Code loads it when a task matches its description.
Run `npx skills add rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a codex`. Or copy the skill folder (.agents/skills/avoiding-subcomposition-pitfalls in rosuH/EasyWatermark) into .agents/skills/avoiding-subcomposition-pitfalls 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 rosuH/EasyWatermark --skill avoiding-subcomposition-pitfalls -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/avoiding-subcomposition-pitfalls, .gemini/skills/avoiding-subcomposition-pitfalls, .github/skills/avoiding-subcomposition-pitfalls and .opencode/skills/avoiding-subcomposition-pitfalls in your project.
SKILL.md names no scripts, command-line tools or credentials: Avoiding Subcomposition Pitfalls is instructions for the agent only.
SKILL.md names 4 domains. As links in the text: developer.android.com, cs.android.com, chrisbanes.me and speakerdeck.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.
Avoiding Subcomposition Pitfalls 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 4.7k tokens (SKILL.md is roughly 19k 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 Avoiding Subcomposition Pitfalls: React Native Best Practices (vercel-labs/openreview, 1.7k stars), Swiftui Pro (twostraws/SwiftUI-Agent-Skill, 5k stars), Ip As Logo (KartikLabhshetwar/better-shot, 2.4k stars) and Compose Multiplatform Patterns (monta-app/ocpp-emulator, 179 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
rosuH (a GitHub user) maintains it in rosuH/EasyWatermark, which has 1,894 GitHub stars. The repository holds 28 skills in this directory. The repository was last updated on October 6, 2026.
Source: rosuH/EasyWatermark on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.