Checkbox
A control element that allows for multiple selections within a set.
Anatomy
To set up the checkbox correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Design impact on the asChild property
The Checkbox.Root element of the checkbox is a label element. This is because the checkbox is a form control and
should be associated with a label to provide context and meaning to the user. Otherwise, the HTML and accessibility
structure will be invalid.
If you need to use the
asChildproperty, make sure that thelabelelement is the direct child of theCheckbox.Rootcomponent.
Examples
Learn how to use the Checkbox component in your project. Let's take a look at the most basic example:
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
export const Basic = () => (
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const Basic = () => (
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<Checkbox.Root>
<Checkbox.Label>Accept terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
Controlled
Use the checked and onCheckedChange props to programatically control the checkbox's state.
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import { useState } from 'react'
export const Controlled = () => {
const [checked, setChecked] = useState<Checkbox.CheckedState>(true)
return (
<Checkbox.Root checked={checked} onCheckedChange={(e) => setChecked(e.checked)}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
}
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
export const Controlled = () => {
const [checked, setChecked] = createSignal<Checkbox.CheckedState>(true)
return (
<Checkbox.Root checked={checked()} onCheckedChange={(e) => setChecked(e.checked)}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
}
<script setup lang="ts">
import { Checkbox, type CheckboxCheckedState } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import { ref } from 'vue'
const checked = ref<CheckboxCheckedState>(true)
</script>
<template>
<Checkbox.Root v-model:checked="checked">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
let checked = $state(false)
</script>
<Checkbox.Root bind:checked>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label>Controlled checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
Indeterminate
Use the indeterminate prop to create a checkbox in an indeterminate state (partially checked).
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-react'
export const Indeterminate = () => (
<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-solid'
export const Indeterminate = () => (
<Checkbox.Root defaultChecked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-svelte'
</script>
<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
Checkbox Group
Ark provides a Checkbox.Group component to manage a group of checkboxes. The Checkbox.Group component manages the
state of the checkboxes and provides a way to access the checked values:
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const Group = () => (
<Checkbox.Group defaultValue={['react']} name="framework" onValueChange={console.log}>
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.Group>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
export const Group = () => (
<Checkbox.Group defaultValue={['react']} name="framework" onValueChange={console.log}>
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<Checkbox.Group :defaultValue="['react']" name="framework" @valueChange="console.log">
<Checkbox.Root v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
</script>
<Checkbox.Group>
{#each items as item}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
Access Checkbox state
Use the Checkbox.Context and its render prop to access the checkbox's state and methods.
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
export const RenderProp = () => (
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{(checkbox) => <Checkbox.Label>Checkbox {checkbox.checked.toString()}</Checkbox.Label>}
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const RenderProp = () => (
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{(checkbox) => <Checkbox.Label>Checkbox {checkbox().checked.toString()}</Checkbox.Label>}
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context v-slot="checkbox">
<Checkbox.Label>Checkbox {{ checkbox.checked.toString() }}</Checkbox.Label>
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator><CheckIcon /></Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{#snippet render(api)}
<Checkbox.Label>Checkbox {api().checked.toString()}</Checkbox.Label>
{/snippet}
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
Field
The checkbox integrates smoothly with the Field component to handle form state, helper text, and error text for proper
accessibility.
import { Checkbox } from '@ark-ui/react/checkbox'
import { Field } from '@ark-ui/react/field'
import { CheckIcon, MinusIcon } from 'lucide-react'
export const WithField = (props: Field.RootProps) => (
<Field.Root {...props}>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { Field } from '@ark-ui/solid/field'
import { CheckIcon, MinusIcon } from 'lucide-solid'
export const WithField = (props: Field.RootProps) => (
<Field.Root {...props}>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { Field, type FieldRootProps } from '@ark-ui/vue/field'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
const props = defineProps<FieldRootProps>()
</script>
<template>
<Field.Root v-bind="props">
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { Field } from '@ark-ui/svelte/field'
import { CheckIcon, MinusIcon } from 'lucide-svelte'
</script>
<Field.Root>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
Root Provider
Use the useCheckbox hook to create the checkbox store and pass it to the Checkbox.RootProvider component. This
allows you to have maximum control over the checkbox programmatically.
import { Checkbox, useCheckbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
export const RootProvider = () => {
const checkbox = useCheckbox()
return (
<>
<span>{checkbox.checked ? 'Checked' : 'UnChecked'}</span>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
import { Checkbox, useCheckbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const RootProvider = () => {
const checkbox = useCheckbox()
return (
<>
<span>{checkbox().checked ? 'Checked' : 'UnChecked'}</span>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
<script setup lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const checkbox = useCheckbox()
</script>
<template>
<span>{{ checkbox.checked ? 'Checked' : 'UnChecked' }}</span>
<Checkbox.RootProvider :value="checkbox">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const id = $props.id()
const checkbox = useCheckbox({ id })
</script>
<span>{checkbox().checked ? 'Checked' : 'UnChecked'}</span>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
If you're using the
Checkbox.RootProvidercomponent, you don't need to use theCheckbox.Rootcomponent.
API Reference
Root
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
checked | CheckedStateThe controlled checked state of the checkbox | |
defaultChecked | CheckedStateThe initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox. | |
disabled | booleanWhether the checkbox is disabled | |
form | stringThe id of the form that the checkbox belongs to. | |
id | stringThe unique identifier of the machine. | |
ids | Partial<{ root: string; hiddenInput: string; control: string; label: string }>The ids of the elements in the checkbox. Useful for composition. | |
invalid | booleanWhether the checkbox is invalid | |
name | stringThe name of the input field in a checkbox. Useful for form submission. | |
onCheckedChange | (details: CheckedChangeDetails) => voidThe callback invoked when the checked state changes. | |
readOnly | booleanWhether the checkbox is read-only | |
required | booleanWhether the checkbox is required | |
value | 'on' | stringThe value of checkbox input. Useful for form submission. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "indeterminate" | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
Control
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "indeterminate" | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
Group
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
defaultValue | string[]The initial value of `value` when uncontrolled | |
disabled | booleanIf `true`, the checkbox group is disabled | |
invalid | booleanIf `true`, the checkbox group is invalid | |
name | stringThe name of the input fields in the checkbox group (Useful for form submission). | |
onValueChange | (value: string[]) => voidThe callback to call when the value changes | |
readOnly | booleanIf `true`, the checkbox group is read-only | |
value | string[]The controlled value of the checkbox group |
HiddenInput
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Indicator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
indeterminate | boolean |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "indeterminate" | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
Label
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "indeterminate" | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseCheckboxReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Accessibility
Complies with the Checkbox WAI-ARIA design pattern.
Keyboard Support
| Key | Description |
|---|---|
Space | Toggle the checkbox |