kmp-compose-state-hoisting

Implements state hoisting patterns for Compose Multiplatform composables to enable testable, stateless UI components.

2|Updated Jun 6, 2026
One-click install
npx skills add https://github.com/ronjunevaldoz/kmp-agent-skills --skill kmp-compose-state-hoisting-ronjunevaldoz
Or copy as Structured Prompt for Agent▼
Please help me install this Agent Skill.
Skill: kmp-compose-state-hoisting
Source: https://github.com/ronjunevaldoz/kmp-agent-skills/tree/main/skills/kmp-compose-state-hoisting
Command: npx skills add https://github.com/ronjunevaldoz/kmp-agent-skills --skill kmp-compose-state-hoisting-ronjunevaldoz

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill includes references (resource) components.

What problem does it solve? Deciding where state should live in a Compose component tree is a common source of untestable, hard-to-preview composables. This Skill provides clear rules for state hoisting in Compose Multiplatform so state ends up at the right level — not buried in leaf composables and not bloating ViewModels with ephemeral UI state. ## Core Features & Use Cases - Hoist-Until-Shared Rule: Move state to the lowest common ancestor of all consumers, with concrete examples for local, sibling-shared, and ViewModel-level state. - Controlled Component Pattern: Build stateless composables using the value + onValueChange contract so parents can validate, transform, and reset values. - Performance Guidance: Apply derivedStateOf for memoized derived state and @Stable/@Immutable annotations to enable recomposition skipping. - Use Case: You have a SearchBar composable with internal state that siblings also need. Apply the hoisting rule to lift the query state to the parent, making SearchBar stateless, previewable, and unit-testable without a ViewModel. ## Quick Start Ask the agent to refactor a stateful composable into a stateless one by hoisting its state to the appropriate ancestor.

Frequently Asked Questions about kmp-compose-state-hoisting

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I hoist state in Jetpack Compose?▼

Hoist state by moving it to the lowest common ancestor of all composables that read or write it. Convert the composable to a stateless form that receives the value and an onValueChange-style callback, so the caller owns the state and the composable becomes a pure render function.

What is the difference between stateful and stateless composables?▼

A stateful composable owns its state internally with remember and mutableStateOf, so callers cannot observe or control it. A stateless composable receives state as parameters plus change callbacks, making it testable, previewable, and shareable across siblings.

When should I use derivedStateOf in Compose?▼

Use derivedStateOf when a computed value changes less often than its input states, such as a form validity flag or a filtered list. Always wrap it in remember, otherwise a new DerivedState object is created on every recomposition and the memoization is lost.

When should state stay in a composable instead of a ViewModel?▼

Keep state in local remember when it is purely ephemeral UI state like dropdown open flags, tooltip visibility, focus, or scroll position. Move state to a ViewModel only when it involves async data, persists across navigation, or is shared with another screen.

Why does my composable recompose even when its data has not changed?▼

Compose marks parameters containing List, Map, or interface types as unstable, which disables recomposition skipping. Annotate the data class with @Stable or @Immutable, or use immutable collection types, so the compiler can skip the composable when inputs are unchanged.

How do I test a stateless composable without a ViewModel?▼

Pass fixed state values directly to the stateless composable in a ComposeTestRule test and assert the rendered output. Verify callbacks by capturing invocations in local variables, since the composable is a pure function of its inputs.