Compose Expert Skill
Non-opinionated, practical guidance for writing correct, performant Compose code —
across Android, Desktop, iOS, and Web. Covers Jetpack Compose and Compose Multiplatform.
Backed by analysis of actual source code from androidx/androidx and
JetBrains/compose-multiplatform-core.
Workflow
When helping with Compose code, follow this checklist:
1. Understand the request
- What Compose layer is involved? (Runtime, UI, Foundation, Material3, Navigation)
- Is this a state problem, layout problem, performance problem, or architecture question?
- Is this Android-only or Compose Multiplatform (CMP)?
2. Analyze the design (if visual reference provided)
- If the user shares a Figma frame, screenshot, or design spec, consult
references/design-to-compose.md
- Decompose the design into a composable tree
- Transcribe, don't adapt — copy, casing, punctuation and number format come across exactly; resolve each value to the token that matches it exactly, and add a token the theme lacks rather than substituting the nearest (
references/design-to-compose.md)
- Map design tokens to MaterialTheme, spacing to CompositionLocals
- Identify animation needs —
references/animation.md (contentKey, phase rule, M3 motion tokens), references/animation-recipes.md (shimmer/loading-crossfade + choreography), or references/animation-advanced.md (shared-element, predictive back, drawBehind-for-color)
3. Consult the right reference
Read the relevant reference file(s) from references/ before answering:
| Topic |
Reference File |
State-hoisting boundary (UI-value-drives-business-logic), derivedStateOf-capture trap, cross-phase back-writing, durable-state-over-events, @ReadOnlyComposable |
references/state-management.md |
Slot / content-API authoring — receiver scopes (RowScope), optional (nullable) slots, XxxDefaults, slot-vs-boolean-flag |
references/view-composition.md |
| Screen/content split (private content composable), framework-state-stays-in-UI (not hoisted to the ViewModel) |
references/screen-structure.md |
| Modifier-as-API-contract rules (no hardcoded placement on a reusable root, caller-modifier-first) + chain ordering |
references/modifiers.md |
Effects (LaunchedEffect/DisposableEffect/SideEffect), lifecycle effects (LifecycleResumeEffect), effect anti-patterns |
references/side-effects.md |
compositionLocalOf vs staticCompositionLocalOf (recomposition scope), custom locals, no-mutable-State-in-a-local |
references/composition-locals.md |
LazyList perf traps — indexOf()-O(n²), no-new-objects-in-key, animateItem, ReportDrawnWhen, infinite-scroll trigger |
references/lists-scrolling.md |
Navigation 3 (NavDisplay, back-stack-as-state, compose-shape guardrails); type-safe @Serializable routes |
references/navigation.md |
AnimatedContent contentKey-on-shape, defer-reads-to-latest-phase (lambda modifiers), M3 motion/easing tokens |
references/animation.md |
| Animation recipes (shimmer/loading-crossfade), sequential/parallel/staggered choreography |
references/animation-recipes.md |
Shared-element transitions (sharedBounds/sharedElement/skipToLookaheadSize), drawBehind-for-animated-color, predictive back |
references/animation-advanced.md |
Extending the theme beyond M3's three slots — custom design tokens via CompositionLocal |
references/theming-material3.md |
| Touch targets, spacing, canonical layouts, foldables, M3 compliance audit |
android-skills:android-ux |
| Recomposition skipping, stability, baseline profiles, benchmarking |
references/performance.md |
Traversal order (traversalIndex / isTraversalGroup), live-region mode (Polite vs Assertive) |
references/accessibility.md |
FocusRequester, focusable(), focusProperties, key events, D-pad, TV, keyboard, focus restoration |
references/focus-navigation.md |
| Removed/replaced APIs, migration paths from older Compose versions |
references/deprecated-patterns.md |
Styles API (experimental): Style {}, MutableStyleState, Modifier.styleable() |
references/styles-experimental.md |
Transcribing a design — exact copy/casing/number format, exact-token-vs-near-neighbour, missing-token-added-not-substituted, export-over-render as source of truth; Figma dropShadow/innerShadow (1.9+, chain placement), spacing/elevation design-token CompositionLocal |
references/design-to-compose.md |
| Production crash patterns, defensive coding, state/performance rules |
references/production-crash-playbook.md |
CMP gotchas (collectAsState-in-commonMain, commonMain @Preview package, Lottie→Kottie, compiler-stability-non-JVM) + Android-only→CMP migration |
references/multiplatform.md |
| Desktop (Window, Tray, MenuBar), iOS (UIKitView), Web (ComposeViewport) |
references/platform-specifics.md |
4. Apply and verify
- Write code that follows the patterns in the reference
- Flag any anti-patterns you see in the user's existing code
- Suggest the minimal correct solution — don't over-engineer
5. Cite the source
When referencing Compose internals, point to the exact source file:
// See: compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
Key Principles
Compose thinks in three phases: Composition → Layout → Drawing. State reads in each
phase only trigger work for that phase and later ones.
Recomposition is frequent and cheap — but only if you help the compiler skip unchanged
scopes. Use stable types, avoid allocations in composable bodies.
Modifier order matters. Modifier.padding(16.dp).background(Color.Red) is visually
different from Modifier.background(Color.Red).padding(16.dp).
State should live as low as possible and be hoisted only as high as needed. Don't put
everything in a ViewModel just because you can.
Side effects exist to bridge Compose's declarative world with imperative APIs. Use the
right one for the job — misusing them causes bugs that are hard to trace.
Compose Multiplatform shares the runtime but not the platform. UI code in
commonMain is portable. Platform-specific APIs (LocalContext, BackHandler,
Window) require expect/actual or conditional source sets.
Source Code Verification
Always verify against live source code — never rely on training data alone.
Tier 1 (Preferred): android-sources MCP server
When available, use the MCP tools for fast, precise lookups:
lookup_class(className: "LazyListState")
lookup_method(className: "Composer", methodName: "startRestartGroup")
search_in_source(query: "fun rememberLazyListState")
list_class_members(className: "Modifier")
get_class_hierarchy(className: "LazyListState")
find_references(className: "SnapshotState", methodName: "value")
Tier 2 (Fallback): Raw GitHub URLs
If the MCP server is unavailable, fetch source directly:
- AndroidX:
https://raw.githubusercontent.com/androidx/androidx/androidx-main/{path}
- Directory listing:
gh api repos/androidx/androidx/contents/{path}
- CMP:
https://raw.githubusercontent.com/JetBrains/compose-multiplatform-core/jb-main/{path}
- AOSP platform:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/{path}?format=TEXT (base64)
Two-layer approach
- Start with guidance — read the topic-specific reference (e.g.,
references/state-management.md)
- Verify against live source — use MCP tools or raw GitHub to confirm behavior
Source tree map
androidx/androidx (branch: androidx-main)
├── compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/
├── compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/
├── compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/
├── compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/
├── compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/
└── compose/navigation/navigation-compose/src/commonMain/kotlin/androidx/navigation/compose/
compose-multiplatform-core (branch: jb-main)
├── compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/
├── compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/window/
├── compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/
├── compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/
└── compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/
compose-multiplatform (resources library)
└── components/resources/library/src/commonMain/
Authoritative Docs
For guidance, best practices, or migration guides — things source code alone can't answer — prefer Google's Android Knowledge Base over web search:
android docs search "LazyColumn performance" # ranked kb:// URLs + summaries
android docs fetch kb://android/develop/ui/compose/lists # full content of a result
4800+ curated docs across Android, Wear, TV, KMP, and Glance. Use this when the internal files in references/ don't cover your question; use source code lookups (above) when you need implementation details rather than guidance.
1---2name: compose3description: Compose and Compose Multiplatform expert for UI development across Android, Desktop, iOS, and Web. Covers state management, composition, animations, navigation, performance, design-to-code workflows, and production crash patterns, backed by source analysis from androidx/androidx and JetBrains/compose-multiplatform-core. Use whenever the user mentions Compose, @Composable, remember, LaunchedEffect, Scaffold, NavHost, NavDisplay, MaterialTheme, LazyColumn, Modifier, recomposition, Compose Multiplatform/CMP, commonMain, expect/actual, ComposeUIViewController, UIKitView, ComposeViewport, Res.drawable/Res.string, or any Compose API. Also trigger on phrases like "design to compose", "build this UI", "implement this design", or any modern Kotlin UI question — including casual mentions like "my compose screen is slow". Plus focus topics: FocusRequester, focusProperties, onPreviewKeyEvent, D-pad, TV remote, ChromeOS, androidx.tv.material3.4---56# Compose Expert Skill78Non-opinionated, practical guidance for writing correct, performant Compose code —9across Android, Desktop, iOS, and Web. Covers Jetpack Compose and Compose Multiplatform.10Backed by analysis of actual source code from `androidx/androidx` and11`JetBrains/compose-multiplatform-core`.1213## Workflow1415When helping with Compose code, follow this checklist:1617### 1. Understand the request18- What Compose layer is involved? (Runtime, UI, Foundation, Material3, Navigation)19- Is this a state problem, layout problem, performance problem, or architecture question?20- Is this Android-only or Compose Multiplatform (CMP)?2122### 2. Analyze the design (if visual reference provided)23- If the user shares a Figma frame, screenshot, or design spec, consult `references/design-to-compose.md`24- Decompose the design into a composable tree25- Transcribe, don't adapt — copy, casing, punctuation and number format come across exactly; resolve each value to the token that matches it exactly, and add a token the theme lacks rather than substituting the nearest (`references/design-to-compose.md`)26- Map design tokens to MaterialTheme, spacing to CompositionLocals27- Identify animation needs — `references/animation.md` (contentKey, phase rule, M3 motion tokens), `references/animation-recipes.md` (shimmer/loading-crossfade + choreography), or `references/animation-advanced.md` (shared-element, predictive back, `drawBehind`-for-color)2829### 3. Consult the right reference30Read the relevant reference file(s) from `references/` before answering:3132| Topic | Reference File |33|-------|---------------|34| State-hoisting boundary (UI-value-drives-business-logic), `derivedStateOf`-capture trap, cross-phase back-writing, durable-state-over-events, `@ReadOnlyComposable` | `references/state-management.md` |35| Slot / content-API authoring — receiver scopes (`RowScope`), optional (nullable) slots, `XxxDefaults`, slot-vs-boolean-flag | `references/view-composition.md` |36| Screen/content split (private content composable), framework-state-stays-in-UI (not hoisted to the ViewModel) | `references/screen-structure.md` |37| Modifier-as-API-contract rules (no hardcoded placement on a reusable root, caller-modifier-first) + chain ordering | `references/modifiers.md` |38| Effects (`LaunchedEffect`/`DisposableEffect`/`SideEffect`), lifecycle effects (`LifecycleResumeEffect`), effect anti-patterns | `references/side-effects.md` |39| `compositionLocalOf` vs `staticCompositionLocalOf` (recomposition scope), custom locals, no-mutable-State-in-a-local | `references/composition-locals.md` |40| LazyList perf traps — `indexOf()`-O(n²), no-new-objects-in-key, `animateItem`, `ReportDrawnWhen`, infinite-scroll trigger | `references/lists-scrolling.md` |41| Navigation 3 (`NavDisplay`, back-stack-as-state, compose-shape guardrails); type-safe `@Serializable` routes | `references/navigation.md` |42| `AnimatedContent` contentKey-on-shape, defer-reads-to-latest-phase (lambda modifiers), M3 motion/easing tokens | `references/animation.md` |43| Animation recipes (shimmer/loading-crossfade), sequential/parallel/staggered choreography | `references/animation-recipes.md` |44| Shared-element transitions (`sharedBounds`/`sharedElement`/`skipToLookaheadSize`), `drawBehind`-for-animated-color, predictive back | `references/animation-advanced.md` |45| Extending the theme beyond M3's three slots — custom design tokens via `CompositionLocal` | `references/theming-material3.md` |46| Touch targets, spacing, canonical layouts, foldables, M3 compliance audit | `android-skills:android-ux` |47| Recomposition skipping, stability, baseline profiles, benchmarking | `references/performance.md` |48| Traversal order (`traversalIndex` / `isTraversalGroup`), live-region mode (`Polite` vs `Assertive`) | `references/accessibility.md` |49| `FocusRequester`, `focusable()`, `focusProperties`, key events, D-pad, TV, keyboard, focus restoration | `references/focus-navigation.md` |50| Removed/replaced APIs, migration paths from older Compose versions | `references/deprecated-patterns.md` |51| **Styles API** (experimental): `Style {}`, `MutableStyleState`, `Modifier.styleable()` | `references/styles-experimental.md` |52| Transcribing a design — exact copy/casing/number format, exact-token-vs-near-neighbour, missing-token-added-not-substituted, export-over-render as source of truth; Figma `dropShadow`/`innerShadow` (1.9+, chain placement), spacing/elevation design-token CompositionLocal | `references/design-to-compose.md` |53| Production crash patterns, defensive coding, state/performance rules | `references/production-crash-playbook.md` |54| CMP gotchas (`collectAsState`-in-commonMain, commonMain `@Preview` package, Lottie→Kottie, compiler-stability-non-JVM) + Android-only→CMP migration | `references/multiplatform.md` |55| Desktop (Window, Tray, MenuBar), iOS (UIKitView), Web (ComposeViewport) | `references/platform-specifics.md` |5657### 4. Apply and verify58- Write code that follows the patterns in the reference59- Flag any anti-patterns you see in the user's existing code60- Suggest the minimal correct solution — don't over-engineer6162### 5. Cite the source63When referencing Compose internals, point to the exact source file:64```65// See: compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt66```6768## Key Principles69701. **Compose thinks in three phases**: Composition → Layout → Drawing. State reads in each71 phase only trigger work for that phase and later ones.72732. **Recomposition is frequent and cheap** — but only if you help the compiler skip unchanged74 scopes. Use stable types, avoid allocations in composable bodies.75763. **Modifier order matters**. `Modifier.padding(16.dp).background(Color.Red)` is visually77 different from `Modifier.background(Color.Red).padding(16.dp)`.78794. **State should live as low as possible** and be hoisted only as high as needed. Don't put80 everything in a ViewModel just because you can.81825. **Side effects exist to bridge Compose's declarative world with imperative APIs**. Use the83 right one for the job — misusing them causes bugs that are hard to trace.84856. **Compose Multiplatform shares the runtime but not the platform**. UI code in86 `commonMain` is portable. Platform-specific APIs (`LocalContext`, `BackHandler`,87 `Window`) require `expect`/`actual` or conditional source sets.8889## Source Code Verification9091Always verify against **live source code** — never rely on training data alone.9293### Tier 1 (Preferred): `android-sources` MCP server9495When available, use the MCP tools for fast, precise lookups:9697```98lookup_class(className: "LazyListState")99lookup_method(className: "Composer", methodName: "startRestartGroup")100search_in_source(query: "fun rememberLazyListState")101list_class_members(className: "Modifier")102get_class_hierarchy(className: "LazyListState")103find_references(className: "SnapshotState", methodName: "value")104```105106### Tier 2 (Fallback): Raw GitHub URLs107108If the MCP server is unavailable, fetch source directly:109110- **AndroidX**: `https://raw.githubusercontent.com/androidx/androidx/androidx-main/{path}`111- **Directory listing**: `gh api repos/androidx/androidx/contents/{path}`112- **CMP**: `https://raw.githubusercontent.com/JetBrains/compose-multiplatform-core/jb-main/{path}`113- **AOSP platform**: `https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/{path}?format=TEXT` (base64)114115### Two-layer approach1161. **Start with guidance** — read the topic-specific reference (e.g., `references/state-management.md`)1172. **Verify against live source** — use MCP tools or raw GitHub to confirm behavior118119### Source tree map120```121androidx/androidx (branch: androidx-main)122├── compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/123├── compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/124├── compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/125├── compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/126├── compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/127└── compose/navigation/navigation-compose/src/commonMain/kotlin/androidx/navigation/compose/128129compose-multiplatform-core (branch: jb-main)130├── compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/131├── compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/window/132├── compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/133├── compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/134└── compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/135136compose-multiplatform (resources library)137└── components/resources/library/src/commonMain/138```139140## Authoritative Docs141142For guidance, best practices, or migration guides — things source code alone can't answer — prefer Google's Android Knowledge Base over web search:143144```bash145android docs search "LazyColumn performance" # ranked kb:// URLs + summaries146android docs fetch kb://android/develop/ui/compose/lists # full content of a result147```1481494800+ curated docs across Android, Wear, TV, KMP, and Glance. Use this when the internal files in `references/` don't cover your question; use source code lookups (above) when you need implementation details rather than guidance.