Jetpack Compose
Declarative UI in Kotlin — how composition differs from the imperative View system, recomposition and what triggers an unnecessary one, remember versus a plain local variable, Compose Multiplatform, and Compose Hot Reload.
7 questions
JuniorTheoryVery commonWhat does remember do that a plain local var in a composable cannot?
What does remember do that a plain local var in a composable cannot?
A plain local var is re-initialized on every recomposition, so it holds nothing. remember caches a value across recompositions; rememberSaveable additionally survives configuration change and process death, via the saved-instance Bundle.
Common mistakes
- ✗Assuming a plain local
varsurvives recomposition and can hold UI state - ✗Thinking
rememberalone survives configuration change and process death - ✗Believing that writing a variable, rather than reading a
State, triggers recomposition
Follow-up questions
- →Why does
rememberSaveablerestrict which types it is able to store? - →When should state live in a
ViewModelinstead of inrememberSaveable?
MiddleTheoryVery commonWhat triggers recomposition, and why does an unstable parameter make it worse?
What triggers recomposition, and why does an unstable parameter make it worse?
Compose re-invokes a composable when the state it reads changes — only those that read it, and only that subtree, as recomposition is skippable. An unstable parameter — a lambda remade each frame, or a type Compose cannot prove stable — kills skipping.
Common mistakes
- ✗Thinking a state change redraws the whole screen rather than the subtrees that read it
- ✗Believing recomposition is triggered by writing state instead of by reading it
- ✗Passing a fresh lambda literal every frame and expecting the child to still skip
Follow-up questions
- →How does the Compose compiler decide a type is stable without an explicit
@Stable? - →Why does hoisting or remembering a lambda restore the child's ability to skip?
JuniorTheoryCommonHow does Compose's declarative UI differ from the imperative View system?
How does Compose's declarative UI differ from the imperative View system?
In Compose the UI is a function of state: you describe the screen for a state, and Compose re-invokes the composable when that state changes. In the View system you hold widget references and mutate them in place, so UI and state can drift apart.
Common mistakes
- ✗Thinking Compose merely wraps the View system and still mutates widgets through setters
- ✗Believing a declarative UI means the whole screen is rebuilt on every frame
- ✗Assuming an XML layout makes the View system declarative in the same sense
Follow-up questions
- →Why must a composable stay free of side effects if Compose may re-invoke it?
- →Where does the state live if a composable holds no mutable widget references?
JuniorTheoryCommonWhat is the difference between Jetpack Compose and Compose Multiplatform?
What is the difference between Jetpack Compose and Compose Multiplatform?
Jetpack Compose is Google's Android UI toolkit. Compose Multiplatform is JetBrains' build of the same model and plugin for Android, iOS, desktop and web. Its Android target is Jetpack Compose; the others bring their own renderer and lose Android APIs.
Common mistakes
- ✗Thinking Compose Multiplatform is a different UI framework that merely shares a name
- ✗Assuming Android-only APIs such as
Contextare available on the iOS or web targets - ✗Believing the Android target of Compose Multiplatform bypasses Jetpack Compose
Follow-up questions
- →Which shared source set do the common composables live in for iOS and Android?
- →What does the iOS target draw with, given there is no Android view system there?
MiddleTheoryOccasionalWhat does Compose Hot Reload do, and which targets can actually use it?
What does Compose Hot Reload do, and which targets can actually use it?
It re-applies changed composable code into the running app, preserving the UI state, so a layout tweak shows without relaunch. Stable since 1.0 (Nov 2025), bundled with Compose Multiplatform 1.10, it runs on JVM/desktop only — not Android, iOS or web.
Common mistakes
- ✗Claiming Compose Hot Reload works on an Android app rather than JVM/desktop only
- ✗Thinking it relaunches the app instead of preserving UI state across the edit
- ✗Calling it experimental — it is stable since 1.0 and ships with Compose Multiplatform 1.10.0
Follow-up questions
- →Which kinds of code change still force a full restart instead of a hot reload?
- →Why does previewing UI on Android need a different tool than desktop hot reload?
SeniorDebuggingOccasionalTyping in the search field stutters — find the wasted recomposition and fix it
Typing in the search field stutters — find the wasted recomposition and fix it
Reading query in OrderScreen recomposes its whole body on every keystroke, and sorted is an ordinary local, so the list is re-sorted each time even though orders never changed. Cache the sort with remember(orders), and read query in the smallest composable that needs it.
Common mistakes
- ✗Assuming a derivation written inside a composable runs once, not on every recomposition
- ✗Reading state at the top of a screen and expecting only the child that uses it to recompose
- ✗Treating recomposition count as free and looking only at the cost of drawing the frame
Follow-up questions
- →When is
derivedStateOfthe right tool instead of a plainremember(key)? - →Which tool gives a per-composable recomposition count, and what does a high skipped count mean?
SeniorDesignRareYour team ships an Android order-management app written entirely in Jetpack Compose: about forty screens, plus camera scanning, push notifications and in-app billing. The company now wants an iOS app within two quarters, staffed by one Android engineer and one iOS engineer. The iOS engineer expects platform-native navigation and form controls on the screens users touch most, and the Android app must not regress in build time or size. You have to decide whether the new shared module renders its UI with Compose Multiplatform, or whether Compose stays Android-only and iOS gets its own UI layer. Explain what you would put into the shared module and what you would deliberately leave out, how the camera, push and billing pieces are reached from shared code, which screens (if any) you would render with shared Compose UI, and what you would measure to justify the decision.
Your team ships an Android order-management app written entirely in Jetpack Compose: about forty screens, plus camera scanning, push notifications and in-app billing. The company now wants an iOS app within two quarters, staffed by one Android engineer and one iOS engineer. The iOS engineer expects platform-native navigation and form controls on the screens users touch most, and the Android app must not regress in build time or size. You have to decide whether the new shared module renders its UI with Compose Multiplatform, or whether Compose stays Android-only and iOS gets its own UI layer. Explain what you would put into the shared module and what you would deliberately leave out, how the camera, push and billing pieces are reached from shared code, which screens (if any) you would render with shared Compose UI, and what you would measure to justify the decision.
Share the platform-free layer first — state, view models, business logic — it pays off whatever the UI choice is. Use shared Compose UI only where platform look-and-feel is not a requirement; leave the most-touched iOS screens native. Camera, push and billing stay behind a per-platform implementation. Decide by measuring how much logic is genuinely platform-free.
Common mistakes
- ✗Assuming Android-only APIs such as billing come along for free with shared UI on iOS
- ✗Treating it as an all-or-nothing choice instead of sharing the platform-free layer first
- ✗Believing shared UI is a precondition for sharing view models and business logic
Follow-up questions
- →How would you expose a platform capability such as the camera to the shared module?
- →Which signal, six months in, would make you reverse this decision?