article banner (priority)

Quiz: Do you understand recomposition and stability?

So you think you understand recomposition and stability? Let's find out with those few simple questions:

answers:
    - "a()"
    - "b()"
    - "c()"
    - "d()"
correct:
    - "b()"
    - "c()"
    - "d()"
explanation: "When text gets changed, its reader scope gets recomposed. We read text in Column, but Column is an inline function, so it is Card scope, which includes b(), c(), and d()."
  • type: "single-answer"
    question: "What should be used to store user text input in a form?"
    answers:
    • "remember"
    • "retain"
    • "rememberSaveable"
    • "rememberSerializable"
      correct: "rememberSaveable"
      explanation: "Use rememberSaveable so user input survives configuration changes; String is Bundle-compatible."
  • type: "single-answer"
    question: "What should be used to store an OkHttp WebSocket connection? (you don't want it lost in case od configuration changes)"
    answers:
    • "remember"
    • "retain"
    • "rememberSaveable"
    • "rememberSerializable"
      correct: "retain"
      explanation: "The live socket must remain connected through configuration changes and cannot be saved to a Bundle."
  • type: "single-answer"
    question: "What should be used to store state observed from StateFlow?"
    answers:
    • "remember"
    • "retain"
    • "rememberSaveable"
    • "rememberSerializable"
      correct: "remember"
      explanation: "The data can be re-read from the flow after configuration changes; no need to save it in a Bundle."
  • type: "single-answer"
    question: "What should be used to store scroll state?"
    answers:
    • "remember"
    • "retain"
    • "rememberSaveable"
    • "rememberSerializable"
      correct: "rememberSaveable"
      explanation: "Scroll position should persist through rotations and is already handled via rememberScrollState."
  • type: "single-answer"
    question: "What is the right way to define imageUrl?"
    answers:
    • "val imageUrl = profile.imageUrl"
    • "val imageUrl = remember { profile.imageUrl }"
    • "val imageUrl = remember(profile) { profile.imageUrl }"
    • "val imageUrl = remember(profile.imageUrl) { profile.imageUrl }"
      correct: "val imageUrl = profile.imageUrl"
      explanation: "Accessing profile.imageUrl is lightweight; remembering it adds overhead without benefit."
  • type: "single-answer"
    question: "What is the right way to define initials?"
    answers:
    • |
      undefined
val initials = run {
val (name, surname) = profile.fullName.split(" ")
"${name.first()}${surname.first()}"
}
      - |
        ```kotlin
val initials = remember {
            val (name, surname) = profile.fullName.split(" ")
            "${name.first()}${surname.first()}"
        }
  - |
    ```kotlin
val initials = remember(profile) {
val (name, surname) = profile.fullName.split(" ")
"${name.first()}${surname.first()}"
}
      - |
        ```kotlin
val initials = remember(profile.fullName) {
            val (name, surname) = profile.fullName.split(" ")
            "${name.first()}${surname.first()}"
        }
correct: |
  ```kotlin
val initials = remember(profile.fullName) {
val (name, surname) = profile.fullName.split(" ")
"${name.first()}${surname.first()}"
}
    explanation: "Initials require non-trivial operations on strings; remember them to avoid recomputation, keyed by `profile.fullName` which they depend on."
  - type: "multiple-answer"
    question: |
      Which of those types are **unstable** in Compose?
    answers:
      - "Int"
      - "String"
      - "List<Int>"
      - "State<Int>"
      - "StateFlow<Int>"
      - "() -> Unit"
      - "() -> List<Int>"
      - "Comparator<Int>"
      - "PersistentList<Int?>"
      - "PersistentList<List<Int?>>"
      - "StringBuilder"
    correct:
      - "List<Int>"
      - "StateFlow<Int>"
      - "PersistentList<List<Int?>>"
      - "StringBuilder"
    explanation: ""
  - type: "multiple-answer"
    question: |
      Which of those classes are *unstable* in Compose?
    answers:
      - "data class User(val name: String)"
      - "data class User(var name: String)"
      - "data class User(val name: String, val tags: Set<String>)"
      - "data class User(val nameState: State<String>)"
      - "data class User(val nameState: StateFlow<String>)"
      - "@Stable data class User(var name: String)"
      - "@Immutable data class User(var name: String)"
    correct:
      - "data class User(var name: String)"
      - "data class User(val name: String, val tags: Set<String>)"
      - "data class User(val nameState: StateFlow<String>)"
    explanation: "`class User(val name: String)` is stable, because it only has stable val properties. `class User(var name: String)` isn’t, because it has var property. `class User(val name: String, val tags: Set<String>)` isn’t because it uses Set, which isn’t stable. `class User(val nameState: State<String>)` is, because State is stable in Compose. `class User(val nameState: StateFlow<String>)` isn’t because StateFlow isn’t stable. `@Stable class User(var name: String)` and `@Immutable class User(var name: String)` are stable, because both @Stable and @Immutable enforce stability."
  - type: "multiple-answer"
    question: |
      ```kotlin
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.*
import androidx.compose.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.vector.*
import kotlinx.browser.document
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.lazy.grid.*
import androidx.compose.foundation.shape.*
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.style.*
import androidx.compose.ui.text.input.*
import androidx.lifecycle.compose.*
import androidx.compose.ui.layout.*

//sampleStart
data class UserUiState(
          val user: UserUi,
          val userSettings: UserSettingsUi,
      )

      data class UserUi(
          val name: String,
          val surname: String,
      )

      data class UserSettingsUi(
          val allowNewsletter: Boolean,
      )

      @Composable
      fun UserScreen(viewModel: UserScreenViewModel = injectViewModel()) {
          val uiState: UserUi by viewModel.uiState.collectAsStateWithLifecycle()
          UserCard(uiState.user)
          UserSettings(uiState.userSettings, { viewModel.toggleAllowNewsletter(it) })
      }

      @Composable
      fun UserCard(user: UserUi) {
          Text(user.name)
          Text(user.surname)
      }

      @Composable
      fun UserSettings(userSettings: UserSettingsUi, toggleAllowNewsletter: (Boolean) -> Unit) {
          Switch(
              checked = userSettings.allowNewsletter,
              onCheckedChange = toggleAllowNewsletter
          )
      }
//sampleEnd
  What will recompose if on the view model:

  ```kotlin
_uiState.update { it.copy(user = it.user.copy(name = "New name")) }
    answers:
      - "Text that displays name"
      - "Text that displays surname"
      - "UserCard"
      - "UserScreen"
      - "Switch"
      - "UserSettings, because lambda is used instead of a function reference"
      - "UserSettings, because uiState is unstable"
    correct:
      - "Text that displays name"
      - "UserCard"
      - "UserScreen"
    explanation: "StateFlow with UserUiState is observed where we read it, so in UserScreen, because in UserScreen we call uiState.user and uiState.userSettings. This recomposition propagates to UserCard, because the user inside UserUiState changed, but not to UserSettings. userSettings hasn’t changed, and the lambda expression is stable and memoized by default. Inside UserCard, the first Text gets recomposed, but the second one skips recomposition, because it receives the same value as the previous one."
How is your score?
  • 0-3 If you plan to implement Compose applications, you should better learn about stability and recomposition
  • 4-6 This is nice, but there is room for improvement
  • 7-9 You are doing great, keep it up!
  • 10 Nice! You seem to understand recomposition and stability pretty well
All those topics are covered in the first two days of Advanced Compose.
If you’d like a clear, structured reference for the parts that cause the most confusion, grab the cheat sheet below.
A practical 10-page reference covering:
  • Modifiers (layout, interaction, drawing, transformations)
  • MaterialTheme & CompositionLocal setup
  • Stability rules (what’s stable vs unstable — and why it matters)
  • What actually triggers recomposition
  • Delayed state reads & hoistable stable state patterns
📖 100% free
📬 Join 20k+ Kotlin developers receiving practical weekly insights
❌ No spam. Unsubscribe anytime.
Get your free cheat sheet → Here