Join our Newsletter — 33% off our NHI Course

What is the difference between stable and unstable parameters in Jetpack Compose?

Stable parameters are inputs Compose can trust to remain unchanged between recompositions, so the framework may skip the function and reuse prior values. Unstable parameters are treated as unsafe to skip, so recomposition happens more readily when the parent updates. That distinction directly affects how much work the UI performs and how easily teams can target optimisation.

Why Compose Stability Changes Recomposition Cost

Stable and unstable parameters matter because they influence whether Compose can safely skip work or must re-evaluate more of the UI tree. That has direct consequences for frame timing, state propagation, and how predictable a screen remains as inputs change. The distinction is often misunderstood as a style preference, but it is really a performance and correctness boundary in the runtime model.

Teams usually notice the effect only after a screen starts recomposing more often than expected, rather than when they first introduce the parameter type or state holder. In practice, many development teams encounter the cost of unstable inputs only after performance regressions have already shown up in profiling, rather than through intentional design.

For broader engineering discipline, NIST’s NIST SP 800-53 Rev 5 Security and Privacy Controls is useful as a control-oriented reference for thinking about change sensitivity, verification, and predictable behaviour, even though it is not Compose-specific.

How Stability Affects Skipping, State, and UI Behaviour

Compose treats stability as a signal about whether a parameter can be assumed to behave consistently across recompositions. If a parameter is stable, Compose can often rely on equality and state observation to avoid unnecessary work. If it is unstable, Compose assumes the safer path and recomposes more aggressively because it cannot prove that the input has not changed in a way that matters to the UI.

That model becomes important when a composable is passed data classes, collections, callbacks, or wrapper objects. A value can be logically “the same” to the developer but still look unstable to Compose if the type or usage pattern prevents reliable change tracking. This is why the practical answer is not just “stable means faster.” Stable inputs reduce avoidable recomposition only when the state model and object behaviour actually support that assumption.

  • Stable parameters help Compose skip function bodies when inputs are unchanged in the ways the runtime can trust.
  • Unstable parameters increase recomposition frequency because Compose must assume the input may have changed.
  • Collections and mutable wrappers are common sources of instability when they are passed without a clear stability contract.
  • Stability affects optimisation opportunities, but it does not replace correct state handling or sound UI logic.

In practice, the biggest payoff comes from designing parameter types so their behaviour is obvious to the runtime and to other developers. That may mean passing smaller values, exposing immutable views, or structuring state so only the truly changing parts are recomposed. It also means remembering that stability is about what Compose can trust, not what the developer intends. When that trust is absent, the framework errs on the side of correctness by doing more work.

The model breaks down when teams assume a stable-looking API automatically guarantees efficient recomposition, because the actual behaviour still depends on how the parameter is represented and observed.

When the Stability Rule Is Blurred by Collections, Lambdas, and State Holders

Tighter recomposition control often improves performance, but it also increases the burden on API design and state modelling, so teams have to balance fewer updates against clearer data flow. Collections are a common edge case because a list or map may appear harmless while still behaving in ways that make Compose cautious. Whether the issue is read-only intent, mutation behind an interface, or identity changes between frames, the runtime only sees the observable contract.

There is also some guidance-versus-consensus ambiguity in how aggressively teams should pursue stability. The consensus is that unstable parameters should not be treated as a problem by default; they become a problem when they cause measurable recomposition overhead or hide unnecessary object churn. Lambdas and callbacks can also be misleading: a callback may be cheap to pass, but if it captures changing state too broadly, it can still create noise in the recomposition model.

For mixed screens, the best rule is to isolate the unstable part rather than redesign every parameter for maximum stability. That keeps optimisation local and avoids turning readability into a casualty of premature tuning. When the app uses derived state, snapshot state, or nested composables, the practical question is not whether everything can be made stable, but which inputs actually deserve that effort.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

MITRE ATT&CK address the attack and risk surface, while CIS Controls v8 and NIST CSF 2.0 set the governance and control requirements practitioners need to meet.

Framework Control / Reference Relevance
CIS Controls v8 16 — Application Software Security Compose parameter behaviour affects app-level correctness and efficiency.
Recommendation — Review UI state boundaries so recomposition-sensitive code stays predictable and efficient.
NIST CSF 2.0 PR.IP — Information Protection Processes and Procedures Stable input handling reflects disciplined software change management.
Recommendation — Apply PR.IP to keep UI state patterns consistent and verifiable across releases.
MITRE ATT&CK T1127 — Trusted Developer Utilities Proxy Execution Not directly applicable to Compose stability; omit as no adversary behaviour is central.
Recommendation — Not applicable to this topic.

Practitioner Guidance

What to prioritise: Focus first on parameters that fan out into large or frequently updated composables, because those create the highest recomposition cost when they are unstable. A small instability in a leaf node is often less important than one at a screen boundary.

What to verify: Verify that the apparent stability of a type matches its real runtime behaviour, especially for collections, wrappers, and state holders. If a value is intended to support skipping, confirm that its structure does not force Compose to treat it as changing more often than necessary.

Common mistake: Treating stability as an abstract optimisation goal instead of a property of concrete parameter design. The useful question is not whether an object sounds immutable, but whether Compose can safely rely on it when deciding to skip work.

Practitioner takeaway: Stable parameters are valuable when they let Compose trust the input contract, but the real optimisation win comes from designing state and parameter boundaries so that trust is justified, observable, and limited to the places that actually matter.