---
title: TriStateCheckbox
description: A three-state checkbox component for checked, unchecked, and indeterminate values.
---



```kotlin expandable title="TriStateCheckboxDemo.kt" githubUrl="https://github.com/composablehorizons/compose-unstyled/blob/main/demo/src/commonMain/kotlin/com/composeunstyled/demo/tristatecheckbox/TriStateCheckboxDemo.kt"
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.Check
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Minus
import com.composeunstyled.CheckedIndicator
import com.composeunstyled.StateIndicator
import com.composeunstyled.Text
import com.composeunstyled.UnstyledCheckbox
import com.composeunstyled.UnstyledIcon
import com.composeunstyled.UnstyledTriStateCheckbox
import com.composeunstyled.demo.borderToken
import com.composeunstyled.demo.colors
import com.composeunstyled.demo.contentToken
import com.composeunstyled.demo.surfaceToken
import com.composeunstyled.theme.Theme

@Preview
@Composable
fun TriStateCheckboxDemo() {
  val checkboxOptions = listOf("Option 1", "Option 2", "Option 3", "Option 4")
  var selected by remember { mutableStateOf(listOf(true, true, false, false)) }
  val triState = when {
    selected.all { it } -> ToggleableState.On
    selected.none { it } -> ToggleableState.Off
    else -> ToggleableState.Indeterminate
  }

  Box(
    modifier = Modifier.fillMaxSize(),
    contentAlignment = Alignment.Center,
  ) {
    Column(
      modifier = Modifier
        .widthIn(max = 300.dp)
        .fillMaxWidth()
        .padding(16.dp),
      verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
      UnstyledTriStateCheckbox(
        value = triState,
        onClick = {
          val newState = when (triState) {
            ToggleableState.Off -> true
            ToggleableState.Indeterminate -> true
            ToggleableState.On -> false
          }
          selected = List(checkboxOptions.size) { newState }
        },
        modifier = Modifier.fillMaxWidth(),
        accessibilityLabel = "Select all options",
        indication = null,
      ) {
        Row(
          modifier = Modifier.fillMaxWidth(),
          verticalAlignment = Alignment.CenterVertically,
        ) {
          StateIndicator(
            modifier = Modifier
              .clip(RectangleShape)
              .size(24.dp)
              .background(Theme[colors][surfaceToken], RectangleShape)
              .border(1.dp, Theme[colors][borderToken], RectangleShape),
            indication = LocalIndication.current,
          ) { state ->
            when (state) {
              ToggleableState.On -> UnstyledIcon(
                Lucide.Check,
                contentDescription = null,
                tint = Theme[colors][contentToken],
              )

              ToggleableState.Indeterminate -> UnstyledIcon(
                Lucide.Minus,
                contentDescription = null,
                tint = Theme[colors][contentToken],
              )

              ToggleableState.Off -> Unit
            }
          }

          Spacer(Modifier.width(12.dp))
          Text(
            "Select All",
            color = Theme[colors][contentToken],
          )
        }
      }

      checkboxOptions.forEachIndexed { index, option ->
        UnstyledCheckbox(
          checked = selected[index],
          onCheckedChange = { checked ->
            selected = selected.toMutableList().apply {
              this[index] = checked
            }
          },
          modifier = Modifier.fillMaxWidth(),
          accessibilityLabel = option,
          indication = null,
        ) {
          Row(
            modifier = Modifier.fillMaxWidth().padding(start = 36.dp),
            verticalAlignment = Alignment.CenterVertically,
          ) {
            CheckedIndicator(
              modifier = Modifier
                .clip(RectangleShape)
                .size(24.dp)
                .background(Theme[colors][surfaceToken], RectangleShape)
                .border(1.dp, Theme[colors][borderToken], RectangleShape),
              indication = LocalIndication.current,
            ) {
              UnstyledIcon(
                Lucide.Check,
                contentDescription = null,
                tint = Theme[colors][contentToken],
              )
            }

            Spacer(Modifier.width(12.dp))
            Text(option, color = Theme[colors][contentToken])
          }
        }
      }
    }
  }
}
```



## Installation

```kotlin
implementation("com.composables:composeunstyled-tri-state-checkbox:2.10.0")
```

## Anatomy

```kotlin
UnstyledTriStateCheckbox(
  value = value,
  onClick = onClick,
) {
  StateIndicator {
  }
}
```

## Concepts

- `UnstyledTriStateCheckbox` represents the tri-state checkbox interaction target.
- `StateIndicator` renders content for the current `ToggleableState`.

## Accessibility

Use tri-state checkboxes for parent selection controls where only some child items are selected.

## Code Examples

### Rendering each checkbox state

Use the `StateIndicator` component to render content for each `ToggleableState`:

```kotlin expandable
UnstyledTriStateCheckbox(
  value = value,
  onClick = { toggleParent() },
) {
  StateIndicator { state ->
    when (state) {
      ToggleableState.On -> BasicText("Selected")
      ToggleableState.Off -> BasicText("Not selected")
      ToggleableState.Indeterminate -> BasicText("Partially selected")
    }
  }
}
```

### Building a select-all checkbox

Use the `ToggleableState.Indeterminate` value when only some items are selected:

```kotlin expandable
val selectedCount = selectedItems.count()
val parentState = when (selectedCount) {
  0 -> ToggleableState.Off
  items.size -> ToggleableState.On
  else -> ToggleableState.Indeterminate
}

UnstyledTriStateCheckbox(
  value = parentState,
  onClick = {
    selectedItems = if (parentState == ToggleableState.On) emptySet() else items.toSet()
  },
) {
  StateIndicator { state ->
    BasicText(state.toString())
  }
}
```

## API Reference

### UnstyledTriStateCheckbox

| Parameter | Type | Description |
|-----------|------|-------------|
| `value` | `ToggleableState` |  |
| `onClick` | `() -> Unit` | Callback invoked when the checkbox is clicked |
| `modifier` | `Modifier` | Modifier to be applied to the checkbox |
| `enabled` | `Boolean` | Whether the checkbox is enabled for interaction (defaults to `true`) |
| `interactionSource` | `MutableInteractionSource?` | MutableInteractionSource for handling interactions |
| `indication` | `Indication?` | Visual indication for interactions |
| `accessibilityLabel` | `String?` |  |
| `content` | `TriStateCheckboxScope.() -> Unit` |  |
### TriStateCheckboxScope

| Parameter | Type | Description |
|-----------|------|-------------|
| `value` | `ToggleableState` |  |
| `enabled` | `Boolean` |  |
| `interactionSource` | `MutableInteractionSource` |  |
### TriStateCheckboxScope.StateIndicator

| Parameter | Type | Description |
|-----------|------|-------------|
| `modifier` | `Modifier` | Modifier to be applied to the checkbox |
| `indication` | `Indication?` | Visual indication for interactions |
| `content` | `(ToggleableState) -> Unit` |  |
