Documentation

Build your catalog

Parameters & controls

Explore runtime controls for Compose scene parameters, including strings, enums, sealed objects, custom options, and nullable values.

Parameters And Controls

Scene parameters are declared inside the scene body with delegated properties:

kotlin
internal val buttonPlayground by scene(group = "Buttons") { contentPadding ->
    val enabled: Boolean by param(true)
    var title: String by param("Continue")

    Box(Modifier.padding(contentPadding)) {
        AppButton(
            text = title,
            enabled = enabled,
            onClick = {
                title = if (title == "Continue") "Saved" else "Continue"
            },
        )
    }
}

param(...) delegates can be declared as var. This lets the scene update a parameter from its own code while Composium still exposes the same value in the settings panel.

Parameter names must be unique

Parameter names must be unique within a scene, including parameters declared by helper composables. By default, the name matches the delegated property; pass name to set a different label:

kotlin
val title: String by param("Hello", name = "Title")
val subtitle: String by param("World", name = "Subtitle")

Duplicate names throw IllegalStateException identifying the conflict. This also applies when changing name dynamically. Different scenes can use the same parameter names.

How Composium renders parameter controls

Parameter kind UI control Notes
Boolean Switch Automatic
String Text field Automatic
enum Option chips Values inferred automatically
Sealed object hierarchy Option chips Values inferred automatically from object instances
Nullable supported parameter Checkbox + underlying control Checkbox toggles null-state; do not add null to options manually
Any other type with explicit options Option chips Use listOf(...).toParamOptions() or explicit named values

Important detail: you can use any type as a parameter value, but interactive selection for custom and numeric types requires explicit options unless Composium can infer them automatically.

For referential or non-static types such as Painter, prefer explicit named options.

Automatic options for Boolean, enum, and sealed object hierarchies

kotlin
enum class ButtonVariant {
    Primary,
    Secondary,
    Danger,
}

sealed interface ButtonSize {
    object Small : ButtonSize
    object Medium : ButtonSize
    object Large : ButtonSize
}

internal val buttonPlayground by scene(group = "Buttons") { contentPadding ->
    val enabled: Boolean by param(true)
    val variant: ButtonVariant by param(ButtonVariant.Primary)
    val size: ButtonSize by param(ButtonSize.Medium)

    Box(Modifier.padding(contentPadding)) {
        AppButton(
            enabled = enabled,
            variant = variant,
            size = size,
        )
    }
}

What happens in the UI:

  • enabled is shown as a switch;
  • variant is shown as chips with enum entries;
  • size is shown as chips with inferred sealed object options.

Explicit options for numbers and custom values

For types like Int, Long, Float, Double, or custom objects, define the allowed values explicitly.

Inside scene {} you can convert raw values to List<ParamOption<T>> with toParamOptions(). If you want full control over labels, pass explicit named values instead.

If the parameter type is nullable, pass only the non-null choices. Composium handles the null state through the checkbox automatically.

kotlin
internal val spacingPlayground by scene(group = "Spacing") { contentPadding ->
    val elevation: Int by param(
        default = 0 named "None",
        options = listOf(
            0 named "None",
            2 named "2dp",
            8 named "8dp",
            16 named "16dp",
        ),
    )

    val alpha: Float by param(
        default = 1f,
        options = listOf(0.25f, 0.5f, 0.75f, 1f).toParamOptions(),
    )

    Box(Modifier.padding(contentPadding)) {
        ExampleCard(
            elevation = elevation,
            alpha = alpha,
        )
    }
}

Custom names

You can override how options are shown in the controls UI.

default can also be passed as a named option with infix syntax:

kotlin
val mode by param(
    default = DisplayMode.Grid named "Grid",
    options = listOf(
        DisplayMode.Grid named "Grid",
        DisplayMode.List named "List",
    ),
)

Using a name mapper:

kotlin
val role by param(
    default = UserRole.Member,
    options = listOf(
        UserRole.Admin,
        UserRole.Member,
        UserRole.Guest,
    ).toParamOptions { role ->
        when (role) {
            UserRole.Admin -> "Administrator"
            UserRole.Member -> "Member"
            UserRole.Guest -> "Guest"
        }
    },
)

Using explicit named values:

kotlin
val alignment by param(
    default = AlignmentMode.Center,
    options = listOf(
        AlignmentMode.Start named "Start",
        AlignmentMode.Center named "Center",
        AlignmentMode.End named "End",
    ),
)

Using a named default without explicit options:

kotlin
val leadingIcon: Painter? by param(
    painterResource(R.drawable.solid_attention) named "Attention",
)

For referential values such as Painter, names act as the stable identity for explicit option chips. In these cases, name every option and name the default too when it is declared independently from the option list.

If explicit option names collide inside the same parameter, Composium will append numeric suffixes automatically until every name becomes unique.

Nullable parameters

Nullable parameters get an extra checkbox that controls whether the value is currently null.

kotlin
internal val cardPlayground by scene(group = "Cards") { contentPadding ->
    val maxLines: Int? by param(
        default = null,
        options = listOf(
            1 named "1 line",
            2 named "2 lines",
            3 named "3 lines",
        ),
    )

    val leadingIcon: Painter? by param(
        painterResource(R.drawable.solid_attention) named "Attention",
    )

    val subtitle: String? by param(null)

    Box(Modifier.padding(contentPadding)) {
        ExampleCard(
            maxLines = maxLines,
            leadingIcon = leadingIcon,
            subtitle = subtitle,
        )
    }
}

What happens in the UI:

  • nullable parameters get a checkbox in the control header;
  • unchecked means the parameter is currently null;
  • checked restores the value and shows its regular control.

For nullable parameters with explicit options, do not include null in the list yourself. Pass only non-null values and let Composium manage the null-state toggle.

If you declare a nullable parameter with param(options = ...) and do not provide an explicit default, Composium uses the first option from the list as the initial value (including null if it is first).

Reordering auto-inferred options

For automatically inferred sealed options, you can still override their display order:

By default, auto-inferred sealed options are sorted by their generated names using natural ordering (for example, r2, r10, r100).

kotlin
val size: ButtonSize by param(ButtonSize.Medium) { inferred ->
    inferred.reversed()
}
Documentation follows the project README.Improve this page