Strong Skipping is a mode available in the Compose compiler. When enabled, it
changes the compiler's behavior in two ways:

- Composables with unstable parameters become [skippable](https://developer.android.com/develop/ui/compose/performance/stability#functions)
- Lambdas with unstable captures are [remembered](https://developer.android.com/develop/ui/compose/state#state-in-composables)

| **Note:** Strong Skipping is enabled by default in Kotlin 2.0.20. The full list of options are available in the [Compose Compiler Gradle Plugin reference](https://kotlinlang.org/docs/compose-compiler-options.html).

## Enable strong skipping mode

Strong skipping is enabled by default in Kotlin 2.0.20.

To enable strong skipping for a Gradle module in a release prior to 2.0.20,
include the following option in
the `composeCompiler` block of your Gradle configuration:  

    android { ... }

    composeCompiler {
       enableStrongSkippingMode = true
    }

## Composable skippability

Strong skipping mode relaxes some of the [stability](https://developer.android.com/develop/ui/compose/performance/stability) rules normally applied
by the Compose compiler when it comes to skipping and composable functions. By
default, the Compose compiler marks a composable function as skippable if all of
its arguments have stable values. Strong skipping mode changes this.

With strong skipping enabled, all restartable composable functions become
skippable. This applies whether or not they have unstable parameters.
Non-restartable composable functions remain unskippable.

### When to skip

To determine whether to skip a composable during recomposition, Compose compares
the value of each parameter with their previous values. The type of comparison
depends on the [stability](https://developer.android.com/develop/ui/compose/performance/stability) of the parameter.

- Unstable parameters are compared using instance equality (`===`)
- Stable parameters are compared using object equality (`Object.equals()`)

If all parameters meet these requirements, Compose skips the composable during
recomposition.

You might want a composable to opt out of strong skipping. That is, you might
want a restartable but non-skippable composable. In this case, use the
`@NonSkippableComposable` annotation.  

    @NonSkippableComposable
    @Composable
    fun MyNonSkippableComposable {}

### Annotate classes as stable

If you want an object using object equality instead of instance equality,
continue to annotate the given class with `@Stable`.
An example of when you might
have to do this is when observing an entire list of objects, data sources such
as Room will allocate new objects for every item in the list any time one of
them changes.

## Lambda memoization

Strong skipping mode also enables more [memoization](https://en.wikipedia.org/wiki/Memoization) of lambdas
inside composables. With strong skipping enabled, every lambda inside a
composable function will be automatically remembered.

### Examples

To achieve memoization of lambdas inside composables when using strong skipping,
the compiler wraps your lambda with a `remember` call. It is keyed with the
captures of the lambda.

Consider a case where you have a lambda as in the following example:  

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

With strong skipping enabled, the compiler memoizes the lambda by wrapping it in
a `remember` call:  

    @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. The runtime
compares unstable keys using instance equality. It compares stable keys using
object equality.
| **Note:** This is slightly different to a typical `remember` call where the Compose runtime compares all keys using object equality.

### Memoization and recomposition

This optimization greatly increases the number of composables that the runtime
skips during recomposition. Without memoization, the runtime is much more likely
to allocate a new lambda to any composable that takes a lambda parameter during
recomposition. As a result, the new lambda has parameters that are not equal to
the last composition. This results in recomposition.
| **Note:** A common misconception is that lambdas with unstable captures are themselves unstable objects. This is not true. The Compose compiler always considers lambdas to be stable. However, with Strong skipping disabled, the compiler does not memoize them and the runtime allocates them during recomposition. As such, lambdas with unstable captures cause Compose not to skip composables because they have unequal parameters.

### Avoid memoization

If you have a lambda that you don't want to memoize, use the `@DontMemoize`
annotation.  

    val lambda = @DontMemoize {
        ...
    }

## APK Size

When compiled, skippable Composables result in more generated code than
composables that are not skippable. With strong skipping enabled, the compiler
marks nearly all composables as skippable and wraps all lambdas in a
`remember{...}`. Due to this, enabling strong skipping mode has a very small
impact on the APK size of your application.

Enabling strong skipping in [Now In Android](https://github.com/android/nowinandroid) increased the APK
size by 4kB. The difference in size largely depends on the number of previously
unskippable composables that were present in the given app but should be
relatively minor.