Strong Skipping Mode

Strong Skipping is an experimental mode added to the Compose compiler in version 1.5.4. With strong skipping enabled, the compiler changes its behavior in two ways:

  • Composables with unstable parameters are now skippable
  • Lambdas with unstable captures will be memoized

Warning: We do not currently consider this mode production ready. We have enabled it in the Compose 1.7 alpha and will evaluate it before reaching beta.

Composable skippability

Strong skipping mode relaxes some of the stability rules normally applied by the Compose compiler when it comes to skipping and composable functions. By default, the Compose compiler will mark a composable function as skippable if it only has stable values provided as arguments. Strong skipping mode changes this.

With strong skipping enabled, all restartable composable functions will be skippable, regardless of if they have unstable parameters or not. Non-restartable composable functions remain unskippable.

To determine whether to skip a composable during recomposition, unstable parameters are compared with their previous values using instance equality. Stable parameters continue to be compared with their previous values using object equality - Object.equals().

If all parameters meet these requirements, the composable is skipped during recomposition.

If you want to opt out a composable function from strong skipping, i.e. you want a restartable but non-skippable composable, you can use the @NonSkippableComposable annotation.

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

Do I still need to annotate classes with @Stable?

You will still need to annotate a class with @Stable if you want the object compared with object equality instead of instance equality.

Lambda memoization

Strong skipping mode also enables more aggressive memoization of lambdas inside composable functions. By default, the Compose compiler will memoize lambdas in composable functions that only capture stable values, additionally composable lambdas are always memoized.

Note: Lambdas with no captures are also memoized, however this is done by the Kotlin compiler and not by the Compose compiler plugin.

With strong skipping enabled, lambdas with unstable captures are also memoized.

Effectively this is wrapping your lambda with a remember call, keyed with the captures of the lambda, automatically e.g.

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = {
        use(unstableObject)
        use(stableObject)
    }
}

roughly becomes the following with strong skipping enabled

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = remember(unstableObject, stableObject) {
        {
            use(unstableObject)
            use(stableObject)
        }
    }
}

The keys follow the same comparison rules as composable functions, unstable keys are compared using instance equality and stable keys are compared using object equality.

Note: This is slightly different to a normal remember call where all keys are compared using object equality.

Doing this optimization greatly increases the number of composables that will skip during recomposition as without this memoization, any composable that takes a lambda parameter will most likely have a new lambda allocated during recomposition and therefore will not have equal parameters to the last composition.

Note: A common misconception is that lambdas with unstable captures are themselves unstable objects. This is not true, lambdas are always considered stable, however as they were not memoized and reallocated during recomposition, they lead to composables that were not skipped due to unequal parameters.

If you have a lambda that you do not want memoized, you can use the @DontMemoize annotation.

val lambda = @DontMemoize {
    ...
}

Enabling strong skipping mode

AndroidX repository

We have enabled this option in AndroidX for all Compose modules.

Other Gradle projects

To strong skipping for a gradle module, include:

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() {
    compilerOptions.freeCompilerArgs.addAll(
        "-P",
        "plugin:androidx.compose.compiler.plugins.kotlin:featureFlag=StrongSkipping",
    )
}